I am running a powershell script for 'dropping few databases' that are not in sync in my secondary server. (please refer the code snippet below)
[CmdletBinding()]
Param(
[Parameter(Mandatory = $True)] $DatabaseName
)
$serverInstanceP = "SQLServerTest"
Write-Host "Primary Server Instance is:"$serverInstanceP
#Validating whether the database name is empty string or `NULL` value
if (![string]::IsNullOrEmpty($DatabaseName)) {
#Validating the presence of any special charater mentioned in the database name except "_" (uderscore)
if ($DatabaseName -notmatch "[^a-zA-Z0-9_]") {
Write-Host "A valid database has been provided, which is not null, nor an empty string neither contain any special character"
}
else {
Write-Warning "Database name should not contain any special character"
}
}
else {
Write-Warning "Database name cannot be NULL"
}
Write-Host $DatabaseName "is the current database"
$syncQuery = "
:Connect SQLServerTest
USE [master]
GO
DROP DATABASE [$($DatabaseName)];
GO "
Invoke-Sqlcmd -ServerInstance $serverInstanceP -Database master -Query $syncQuery -QueryTimeout 1200
While running the above script it is giving me an error 'ParserError: (:) [Invoke-Sqlcmd], ParserException'.
Especially ':Connect SQLServerTest'
Do I need to send my query through #' '#
But when I run the same query on the database server through SQLCMD, it is working fine.
Unfortunately, the command ":CONNECT" is not supported by "invoke-sqlcmd".
https://learn.microsoft.com/en-us/powershell/module/sqlserver/invoke-sqlcmd?view=sqlserver-ps
The connection is done with the parameters "-ServerInstance $serverInstanceP -Database master".
Related
Good morning everyone. I am currently in school, working on a PowerShell project, part 2. I was able to get the script to work but now I'm being asked to do this, Check for the existence of a database named ClientDB. Output a message to the console that indicates if the database exists or if it does not. If it already exists, delete it and output a message to the console that it was deleted.Create a new database named “ClientDB” on the Microsoft SQL server instance. Output a message to the console that the database was created. Then go through the rest in creating the table and importing the csv as I've done that much. My problem is how to use the invoke-sqlcmd in an if/else, else/if statement. Thank you so much.
Try {
$DBname = "ClientDB"
$SqlServer = ".\SQLEXPRESS"
if([bool] (Get-SQLDatabase -Filter * | ? {$SqlServer -eq $DBname} ))
{ Write-Host 'ClientDB exists' }
else { Write-Host 'ClientDB does not exists' }
$CreateTableQuery = #"
use $DBname
CREATE TABLE Client_A_Contacts
(
first_name varchar(20),
last_name varchar(20),
city varchar(20),
county varchar(20),
zip varchar(20),
officePhone varchar(20),
mobilePhone varchar(20)
)
"#
Import-Module sqlps -DisableNameChecking
$ServerObject = New-Object Microsoft.SqlServer.Management.Smo.Server($SqlServer)
$DBobject = New-Object Microsoft.SqlServer.Management.Smo.Database($ServerObject, $DBname)
$DBobject.Create()
Write-Host $DBobject "create success # " $DBobject.CreateDate
Invoke-sqlcmd -ServerInstance $SqlServer -Database $DBname -Query $CreateTableQuery
Pop-Location
Import-Csv "NewClientData.csv" | ForEach-Object {Invoke-sqlcmd -ServerInstance $SqlServer -
Database $DBname -Query "insert into dbo.Client_A_Contacts values
('$($_.first_name)','$($_.last_name)','$($_.city)','$($_.county)',
'$($_.zip)','$($_.officePhone)','$($ _.mobilePhone)')"}
}
catch [System.OutOfMemoryException] {
Write-Host "Caught: System.OutOfMemoryException"
}
Invoke-Sqlcmd -Database ClientDB –ServerInstance .\SQLEXPRESS -Query ‘SELECT * FROM dbo.Client_A_Contacts’ > .\SqlResults.txt
I'm trying to work out the PowerShell syntax for cancelling all pending import/export operations on an Azure SQL Server. I know that I can use Stop-AzSqlDatabaseActivity cmdlet but this requires a database name (the database might not already exist so piping Get-AzSqlDatabase won't work). Is there something I can do without specifying the databases, only the server?
Thanks!
Open new PowerShell window, you may use cloud shell on Azure portal as well by clicking the cloud shell button at the top right at your portal screen.
Copy and paste the following PowerShell code and execute it - it will create a function for the current PowerShell session
function Cancel-AzSQLImportExportOperation
{
param
(
[parameter(Mandatory=$true)][string]$ResourceGroupName
,[parameter(Mandatory=$true)][string]$ServerName
,[parameter(Mandatory=$true)][string]$DatabaseName
)
$Operation = Get-AzSqlDatabaseActivity -ResourceGroupName $ResourceGroupName -ServerName $ServerName -DatabaseName $DatabaseName | Where-Object {($_.Operation -like "Export*" -or $_.Operation -like "Import*") -and $_.State -eq "InProgress"}
if(-not [string]::IsNullOrEmpty($Operation))
{
do
{
Write-Host -ForegroundColor Cyan ("Operation " + $Operation.Operation + " with OperationID: " + $Operation.OperationId + " is now " + $Operation.State)
$UserInput = Read-Host -Prompt "Should I cancel this operation? (Y/N)"
} while($UserInput -ne "Y" -and $UserInput -ne "N")
if($UserInput -eq "Y")
{
"Canceling operation"
Stop-AzSqlDatabaseActivity -ResourceGroupName $ResourceGroupName -ServerName $ServerName -DatabaseName $DatabaseName -OperationId $Operation.OperationId
}
else
{"Exiting without cenceling the operation"}
}
else
{
"No import or export operation is now running"
}
}
Cancel-AzSQLImportExportOperation
use the function
Cancel-AzSQLImportExportOperation
to cancel an Import or Export operation you need to provide the Resource Group name, Server name and Database name where the operation is currently running.
Other than the Stop-AzSqlDatabaseActivity cmdlet, you can also use Database Operations-Cancel API Rest API to cancel import or export operations & you need to pass the Database name which is a mandatory parameter As per the current Azure Documentation.
I'm writing some Powershell scripts to manage a SQL Server. When I create my SMO object, I do a test to see if I can list my databases. If so, then I assume I'm connected and call a function (via an included file) that has an Invoke-Sqlcmd that causes my database to disconnect or something.
However, if I run the invoke command directly in the script, it works fine.
Looking at the provided code, my output is as follows:
I'm connected
$SqlServer is not contactable
[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.SqlServer.SMO') | Out-Null
$ErrorActionPreference = "Stop"
Import-Module 'sqlps' -DisableNameChecking #load all of SMO
$ScriptDirectory = Split-Path -Path $MyInvocation.MyCommand.Definition -
Parent
try {
("$ScriptDirectory\_HRbackup.ps1")
("$ScriptDirectory\_HRprinter.ps1")
("$ScriptDirectory\_HRbody.ps1")
} catch {
Write-Host "Error while loading supporting PowerShell Scripts"
}
$ServerList = $env:COMPUTERNAME
$SqlServer = New-Object ('Microsoft.SqlServer.Management.Smo.Server')
$ServerList # add server list
try {
$databases = $SqlServer.Databases
if ($databases.Count -gt 0) {
Write-Host "I am connected"
Backup-Html
} else {
$login = Get-Credential -Message 'Please prov#ide your SQL
Credentials'
$SqlServer.ConnectionContext.LoginSecure = $false
$SqlServer.ConnectionContext.Login = $login.UserName
$SqlServer.ConnectionContext.set_SecurePassword($login.Password)
$SqlServer.ConnectionContext.connect()
}
} catch {
$WarningPreference = 'continue'
Write-Warning "$SqlServer is not contactable"
#$SqlServer.ConnectionContext.Disconnect()
} finally {
$SqlServer.ConnectionContext.Disconnect()
}
Here is the content of Backup-Html:
$Query = "select top 5 a.server_name, a.database_name,
backup_finish_date, a.backup_size,
CASE a.[type] -- Let's decode the three main types of
backup here
WHEN 'D' THEN 'Full'
WHEN 'I' THEN 'Differential'
WHEN 'L' THEN 'Transaction Log'
ELSE a.[type]
END as BackupType
-- Build a path to the backup
,'\\' +
-- lets extract the server name out of the recorded
server and instance name
CASE
WHEN patindex('%\%',a.server_name) = 0 THEN
a.server_name
ELSE
substring(a.server_name,1,patindex('%\%',a.server_name)-1)
END
-- then get the drive and path and file information
+ '\' + replace(b.physical_device_name,':','$') AS
'\\Server\Drive\backup_path\backup_file'
from msdb.dbo.backupset a join msdb.dbo.backupmediafamily
b
on a.media_set_id = b.media_set_id
where a.database_name Like 'Easy%'
order by a.backup_finish_date desc"
#Use SQLCmd to execute the query on the server
Invoke-Sqlcmd -ServerInstance $SQLServer -Query $Query
I am working on a script that will delete App-V keys stored in the registry. When a user opens an application, it creates a key within the following location:
HKLM\SOFTWARE\Microsoft\AppV\MAV\Configuration\Packages\**PackageID**\UserConfigEx\**SID**
The PackageID and the SID are unique each time and I want to be able to delete the SID subkey within each PackageID key.
The user will enter the SID and then I would like to use a wildcard (if possible) to navigate into each Package ID which is present.
So far I have the following:
#Take user input
$SID = Read-Host "Please enter users SID"
$computer = Read-Host "Please enter computer name"
#Test connection
Write-Host "Connecting to $computer"
if (Test-Connection -ComputerName $computer -Quiet -BufferSize 16 -Count 1) {
#Connect to registry and delete key
try
{
$reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey(‘LocalMachine’, $computer)
$regKey = $reg.OpenSubKey(“HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\AppV\\MAV\\Configuration\\Packages\\*\\UserConfigEx\\$SID”,$true )
if ($regkey.GetValue(“$SID”))
{
$regKey.DeleteValue(“$SID”)
Write-Host
Write-Host "$SID key deleted successfully" -ForegroundColor Green
}
else
{
Write-Host
Write-Host "No keys with this SID exist." -ForegroundColor Red
}
} catch {
$ErrorMessage = $_.Exception.Message
Write-Host "Unable to connect to $computer. Error: $($ErrorMessage)." -ForegroundColor Red
}
} else
{
Write-Host "Unable to connect to $computer. Please ensure correct computer name / IP address has been entered correctly." -ForegroundColor Red
}
If I run this I receive:
You cannot call a method on a null-valued expression.
At line:51 char:9
+ if ($regkey.GetValue(“$SID”))
+ ~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
I am using some of the script which I received help with here to remotely connect to the machine.
The .NET registry API doesn't support wildcards (*) in key paths.
As a result, the $regKey.GetValue() failed, because $regKey = $reg.OpenSubKey(...) returned $null due to not finding a key, and calling a method on $null always results in the error message quoted in the question.
By contrast, PowerShell's registry provider, via the *-Item* cmdlets, does, but you need PowerShell remoting in order to use it remotely.
PowerShell remoting is enabled by default on Windows Server 2012 and above; on older OS versions you can enable it by running Enable-PSRemoting on the target machine(s) (requires PSv3+).
With PowerShell remoting enabled, you need to wrap your code in an Invoke-Command -ComputerName <name> { ... } call (to which you may have to pass credentials too).
If enabling PowerShell remoting is not an option, you must emulate wildcard-based matching via a nested loop based on per-element wildcard matching of the results from .GetSubkeyNames().
As an aside: you never need to escape \ as \\ in PowerShell strings; PowerShell uses ` as the escape character inside "...", so the only character you need to escape there is ` itself, as ``.
A PowerShell remoting-based solution:
Note that Invoke-Command -ComputerName ... must be called from an elevated session (Run As Administrator):
try {
Invoke-Command -ErrorAction Stop -ComputerName $computer {
# Define wildcard-based path.
$keyPath = "registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\AppV\MAV\Configuration\Packages\*\UserConfigEx\$SID"
# See if it matches any keys.
if (Test-Path $keyPath) {
# Note: I'm assuming you want to remove the entire *key*.
# To only remove a key's *value*, use Remove-ItemProperty.
Remove-Item -Path $keyPath
} else {
Write-Warning "No keys with SID $SID exist."
}
}
} catch [System.Management.Automation.Remoting.PSRemotingTransportException] {
# Note: Depending on the specifics of your Invoke-Command call, the reason may
# be permissions-related; when in doubt, examine $_
Write-Warning "Unable to connect to $computer. Please ensure correct computer name / IP address has been entered correctly:`n$_"
} catch {
# Other, unexpected failure.
Throw
}
Looks like an ascii vs unicode quotation mark issue:
You have:
$regkey.GetValue(“$SID”)
which should be replaced with:
$regkey.GetValue("$SID")
I'm confused:
The following code produces an error:
try {
invoke-sqlcmd -ServerInstance 'localhost' -Database 'tempdb' -Query 'CREATE TABLE foo (bar TINYINT IDENTITY(1,1) DEFAULT 1);;' -Verbose -ErrorLevel 0 -AbortOnError -ErrorAction Stop -OutputSqlErrors $true -ErrorVariable $err -OutVariable $err -SeverityLevel 0;
"OK";
}
catch {
"ERROR"
$_
}
Result:
ERROR
Invoke-Sqlcmd : Defaults cannot be created on columns with an
IDENTITY attribute. Table 'foo', column 'bar'.
This piece runs apparently smoothly:
try {
invoke-sqlcmd -ServerInstance 'localhost' -Database 'tempdb' -Query 'SELECT CAST(111111111111 AS TinyINT) AS Error' -Verbose -ErrorLevel 0 -AbortOnError -ErrorAction Stop -OutputSqlErrors $true -ErrorVariable $err -OutVariable $err -SeverityLevel 0;
"OK"
}
catch {
"ERROR"
$_
}
Result:
OK
In both cases the catch-block must be used!
It's a known bug with invoke-sqlcmd. I logged it on Connect, the item is marked as fixed but "fixed" in connect terms doesn't necessarily mean the fix has been released or is planned for release in the current version. It just means that it has been fixed internally and they'll release it someday.
As of 4/19/2013, the fix has NOT been released.
I suspect this has something to do with the configuration of the connection initiated by Invoke-SqlCmd; in particular, having ANSI_WARNINGS set to OFF in SQL Server allows your TSQL block to be evaluated as a NULL.
SET ANSI_WARNINGS OFF
SELECT CAST(111111111111 AS TinyINT) AS Error
Returns NULL.