CMD and Powershell (SQL) commands remote computer - sql-server

I am writing script with choices and some of commands are command prompt, CMD SQL and powershell.
POWERSHELL: Invoke-command -computername $computer -scriptblock{Invoke-Sqlcmd -Query "select * from lastreceipt" | where {$_.CreationTime.date -eq '%2017-06-21%'}}
ERROR: Invalid object name 'lastreceipt'.
C:\>sql -p base1//password "select * from lastreceipt where creationtime like '%2017-06-21%' and LOGSTACKID = 'RLOG_WED'"
LEVEL = 00
LOGSTACKID = RLOG_WED
PROGRAMID = SALESSTA
CREATIONTIME = 2017-06-21 00:00:10
TERMINALNUMBER = 2
RECEIPTNUMBER = 3232
RECEIPTTIME = 2017-06-21 21:07:45
OFFSET = 4386
LOGSTATUS = READY
LASTUPDATETIME = 2017-06-21 21:09:00
Works just fine but with powershell can't get it work.
I have over 1000 servers where I need that to work.

ERROR: Invalid object name 'lastreceipt'.
It tells you in the error.. Your query is incorrect.

Related

How to remove encryption from all objects in SQL Server?

I have more than a hundred encrypted procedures and functions that I want to decrypt (I am trying a bacpac file export but it fails due to procedures being encrypted). I tried using dbforge sql decryptor decryption wizard for in place alter but I get the error:
Definition is invalid. Can't find CREATE keyword.
When I try to see the DDL script of a stored procedure(using dbforge sql decryptor), I get the error:
A definition for the object dbo.pt_blocks cannot be shown because it is encrypted by a third party tool
I can not find a resolution to this. Are there any solutions or other tools available for this?
Edit: I found this resource which mentions
take the source code and issue an ALTER command without the encryption option. Just take the source code and remove the WITH ENCRYPTION
How could I achieve this?
EDIT: I have enabled remote DAC. How can I decrypt everything? The accepted answer from this question has a broken link.
Edit: The problem has been solved by uninstalling a third party tool which was creating encrypted procedures.
Below is a PowerShell example that creates a script file of all encrypted objects, gleaned from Paul White's The Internals of WITH ENCRYPTION article. Change the data source and initial catalog in the 2 connection strings to the desired server and database as well as script file path.
A DAC connection is used to retrieve values from system tables so sysadmin server role membership is required. If run remotely, the SQL Server remote admin connections option must be enabled and TCP port 1434 allowed through the firewall.
The script can be run from the PowerShell ISE or from a command prompt after customization. Example command-line invocation, assuming script was saved to file "Decrypt-Objects.ps1".
powershell -ExecutionPolicy RemoteSigned -File C:\PowershellScripts\Decrypt-Objects.ps1
PowerShell script:
# PowerShell implementation of T-SQL code from https://sqlperformance.com/2016/05/sql-performance/the-internals-of-with-encryption
Function Get-DecryptedString($pwd, $data) {
$key = [System.Array]::CreateInstance([int], 256)
$box = [System.Array]::CreateInstance([int], 256)
$cipher = [System.Array]::CreateInstance([byte], $data.Length)
for ($i = 0; $i -lt 256; ++$i) {
$key[$i] = $pwd[$i % $pwd.Length]
$box[$i] = $i
}
for ($j = $i = 0; $i -lt 256; ++$i) {
$j = ($j + $box[$i] + $key[$i]) % 256
$tmp = $box[$i]
$box[$i] = $box[$j]
$box[$j] = $tmp
}
for ($a = $j = $i = 0; $i -lt $data.Length; ++$i) {
++$a
$a %= 256
$j += $box[$a]
$j %= 256
$tmp = $box[$a]
$box[$a] = $box[$j]
$box[$j] = $tmp
$k = $box[(($box[$a] + $box[$j]) % 256)]
$cipher[$i] = ($data[$i] -bxor $k)
}
$decryptedString = [System.Text.Encoding]::Unicode.GetString($cipher)
return $decryptedString
}
Function Get-ClearObjectText($connectionString, $objectName) {
$getRc4KeyQuery = #"
DECLARE
#objectid integer = OBJECT_ID(#ObjectName),
#family_guid binary(16),
#objid binary(4),
#subobjid binary(2);
-- Find the database family GUID
SELECT #family_guid = CONVERT(binary(16), DRS.family_guid)
FROM sys.database_recovery_status AS DRS
WHERE DRS.database_id = DB_ID();
-- Convert object ID to little-endian binary(4)
SET #objid = CONVERT(binary(4), REVERSE(CONVERT(binary(4), #objectid)));
SELECT
-- Read the encrypted value
#imageval = SOV.imageval,
-- Get the subobjid and convert to little-endian binary
#subobjid = CONVERT(binary(2), REVERSE(CONVERT(binary(2), SOV.subobjid)))
FROM sys.sysobjvalues AS SOV
WHERE
SOV.[objid] = #objectid
AND SOV.valclass = 1;
-- Compute the RC4 initialization key
SELECT #RC4key = HASHBYTES('SHA1', #family_guid + #objid + #subobjid);
"#
$connection = New-Object System.Data.SqlClient.SqlConnection($dacConnectionString)
$connection.Open()
$command = New-Object System.Data.SqlClient.SqlCommand($getRc4KeyQuery, $connection)
($command.Parameters.Add("#ObjectName", [System.Data.SqlDbType]::NVarChar, 261)).Value = $objectName
($command.Parameters.Add("#imageval", [System.Data.SqlDbType]::VarBinary, -1)).Direction = [System.Data.ParameterDirection]::Output
($command.Parameters.Add("#RC4key", [System.Data.SqlDbType]::Binary, 20)).Direction = [System.Data.ParameterDirection]::Output
[void]$command.ExecuteNonQuery()
$imageval = $command.Parameters["#imageval"].Value
$RC4key = $command.Parameters["#RC4key"].Value
$connection.Close()
$decryptedString = Get-DecryptedString -pwd $RC4key -data $imageval
Return $decryptedString
}
# ############
# ### MAIN ###
# ############
# DAC connection string for decryption
$dacConnectionString = "Data Source=admin:YourServer;Initial Catalog=YourDatabase;Integrated Security=SSPI"
# normal connection string for encrypted object list
$connectionString = "Data Source=YourServer;Initial Catalog=YourDatabase;Integrated Security=SSPI"
# target file path for clear encrypted objects DDL
$scriptFilePath = "C:\Scripts\EncryptedObjects.sql"
[void](New-Item -Path "C:\Scripts\EncryptedObjects.sql" -ItemType file -Force) # create directory (if needed) and empty script file
$EncryptedObjectQuery = #"
SELECT
QUOTENAME(OBJECT_SCHEMA_NAME(object_id)) + '.' + QUOTENAME(name) AS QualifiedObjectName
FROM sys.objects
WHERE OBJECTPROPERTY(object_id, 'IsEncrypted') = 1;
"#
try {
$connection = New-Object System.Data.SqlClient.SqlConnection($connectionString)
$command = New-Object System.Data.SqlClient.SqlCommand($EncryptedObjectQuery, $connection)
$connection.Open()
$reader = $command.ExecuteReader()
while ($reader.Read()) {
$createObjectScript = Get-ClearObjectText -connectionString $dacConnectionString -objectName $reader["QualifiedObjectName"]
$createObjectScript | Out-File -FilePath $scriptFilePath -Append
"GO" | Out-File -FilePath $scriptFilePath -Append
}
$connection.Close()
}
catch {
throw
}

Query SQL Database and send email for each record - PowerShell

I am trying to query a SQL database and then loop through each of the data sets to send an email with data.
Below is a sample data set - so for record 1, my goal is to send an email to powershell1#whyme.com with the policy information for that specific record and so on for the remaining records.
PolicyNumber PSN TransactionPremium EmailAddress Record
ABC DE3 0000012183 00 8636692 14109 powershell1#whyme.com 1
FGH JI3 0000012183 00 8636693 -14199 powershell2#whyme.com 2
KLM NO3 0000000774 03 8556541 -1664 powershell3#whyme.com 3
PRS TU3 0000000943 03 8579971 0 powershell4#whyme.com 4
HCA HO3 0000000969 03 8603944 -1425 powershell5#whyme.com 5
But after returning the data from the query, I cannot figure out how to loop through each record and send the email.
$Instance = "sqlinstancename"
$Database = "databasename"
$SQL = #'
SELECT TOP (5) PolicyNumber, PSN, CAST(TransactionPremium AS DECIMAL(19,4)) AS TransactionPremium, EmailAddress
FROM dbo.TableName ORDER BY BatchDate DESC
'#
Invoke-DbaQuery -SqlInstance $Instance -Database $Database -Query $SQL
I assume it will be something along the lines of:
ForEach ($i in i)
{
Send-MailMessage -From "noreply#companyx.com" -To $EmailAddress -Subject "Test" -BodyAsHTML $PolicyNumber"
}
Could I get some assistance on how to accomplish this task?
Assuming all is well with the query, a loop to send the emails may look something like this:
$Rows = Query-DataBase Query <Query> -Instance <Server\Instance> -Database <DBName>
ForEach( $Row in $Rows)
{
$EmailParams = #{
$From = 'noreply#companyx.com'
$To = $Row.EmailAddress
$Subject = 'test'
$BodyAsHtml = $true
$Body = $Row.PolicyNumber
$SMTPServer = 'YourSMTPServerFQDN'
}
Send-EmailMessage #EmailParams
}
Note: I'm using splatting which you can read about in about_Splatting. Splatting helps a lot with formatting and readability. That said, this is really just referencing the desired properties in the loop and therefore per each item then sending the email. As such it can easily be expanded so you can get the body, subject, or whatnot correct.

How to pass array as variable in SQLCMD

I am trying to pass server names in sql script , but its not working.
Please help
SQL Script patch_report.sql, I am running via powershell giving error
SELECT * from table where server in ('$(trimsqlstr)')
Error
Msg 102,level Level 15, State 1, Server DBserver, Line 1
Incorrect syntax near 'server1'.
$DB_server = 'DBserver'
$serverName = "server1
server2
server3
"
$serverName = $serverName -split "\n" | foreach {$_.ToString().TrimEnd()}
$trimsqlstr = foreach($server in $serverName){
if ($serverName.Indexof($server) -eq $($serverName.Length-1)){
"'$Server'"
} else {
"'$Server',"
}
SQLCMD.exe -v trimsqlstr = "$($trimsqlstr)" -E -S $DB_server -W -i patch_report.sql
I am expecting it to result like this
SELECT * from table where server in ('server1','server2','server3')
The accepted answer is an excellent solution, I just want to point you towards the -join operator.
The -join operator is very practical for joining members of an array to a string.
You can do something like this:
$serverNames = "server1
server2
server3
"
$serverNameArray = $serverNames -split "\n" | foreach {$_.ToString().TrimEnd()} | Where-Object {$_} | foreach {"'$_'"}
$whereClause = $serverNameArray -join ','
$selectQuery = "SELECT * from table where server in ($whereClause)"
Where-Object {$_} is removing the empty elements.
You may use below code:
$DB_server = 'DBserver'
$serverName = "server1
server2
server3
"
$serverName = $serverName -split "\n" | foreach {$_.ToString().TrimEnd()}
ForEach($server in $serverName)
{
$serverstring = $serverstring+"'"+$server+"'"+","
}
$trimsqlstr = $serverstring.Substring(0, $serverstring.Length-4)
And then use $trimsqlstr in query like below
SELECT * from table where server in ($trimsqlstr)

powershell unable to add ips into a security group via array

Having issues getting this script running. Err. Cannot index into a null array
any ideas would be a great help. I've looked at verbose logging but I'm not sure how to output compute methods to find the contents. Obviously it appears to be empty but for investigation purposes at least it would be a start.
$rgname = "xxxxxx"
$subscriptionname = "xxxxxx"
$vmname = "xxxxxx"
# Get the VM we need to configure
$vm = Get-AzureRmVM -ResourceGroupName $rgname -Name $vmname
Write-host "$vm"
# Get the name of the first NIC in the VM
$nic = Get-AzureRmNetworkInterface -ResourceGroupName $rgname -Name (Get-AzureRmResource -ResourceId $vm.NetworkInterfaceIDs[0]).ResourceName
$nsg = Get-AzureRmNetworkSecurityGroup -ResourceGroupName $rgname -Name (Get-AzureRmResource -ResourceId $nic.NetworkSecurityGroup.Id).Name
$nameAndIPArray = #(("ipname1","ipname2","ipname3","ipname4",ipname5"),
("ip1,"ip2","ip3","ip4","ip5"))
#LOOP THE ARRAY AND SET DESCRIPTION AND IP VARIABLE FOR COMMAND
$priority = 1010
for ($i=0;$i -lt $nameAndIPArray[0].length; $i++) {
$nameAndIPArray[0][$i] + " " + $nameAndIPArray[1][$i]
$nsg | Add-AzureRmNetworkSecurityRuleConfig -Name $nameAndIPArray[0][$i] -Description $nameAndIPArray[0][$i] -Access Allow -Protocol Tcp -Direction Inbound -Priority $priority -SourceAddressPrefix $nameAndIPArray[1][$i] -SourcePortRange * -DestinationAddressPrefix * -DestinationPortRange 443
Set-AzureRmNetworkSecurityGroup -NetworkSecurityGroup $nsg
$priority = $priority + 10
}
Microsoft.Azure.Commands.Compute.Models.PSVirtualMachine
Cannot index into a null array.
At line:14 char:1
Get-AzureRmResource : Cannot validate argument on parameter 'ResourceId'. The argument is null or empty. Provide an argument that is not null or
empty, and then try the command again.
Add-AzureRmNetworkSecurityRuleConfig : Cannot bind argument to parameter 'NetworkSecurityGroup' because it is null.
At line:28 char:12
Set-AzureRmNetworkSecurityGroup : Cannot bind argument to parameter 'NetworkSecurityGroup' because it is null.
At line:29 char:59
I test in my lab, there are some mistakes in your script. Use your script, I could not get $nic and $nsg value.$vm does not have the attribute NetworkInterfaceIDs[0], so you could not use like this. The line $nameAndIPArray loses ". The correct usage should be like below:
$nameAndIPArray = #(("ipname1","ipname2","ipname3","ipname4","ipname5"),
("ip1","ip2","ip3","ip4","ip5"))
I modify your script, I get $nsg by using resource group name and nsg name. You could find them on Portal, it works for me.
$nsg= Get-AzureRmNetworkSecurityGroup -ResourceGroupName <resource group name> -Name "<NSG name>"
$nameAndIPArray = #(("ipname1","ipname2","ipname3","ipname4","ipname5"),
("10.0.0.4","10.0.0.5","10.0.0.6","10.0.0.7","10.0.0.8"))
$priority = 1010
for ($i=0;$i -lt $nameAndIPArray[0].length; $i++) {
$nameAndIPArray[0][$i] + " " + $nameAndIPArray[1][$i]
$nsg | Add-AzureRmNetworkSecurityRuleConfig -Name $nameAndIPArray[0][$i] -Description $nameAndIPArray[0][$i] -Access Allow -Protocol Tcp -Direction Inbound -Priority $priority -SourceAddressPrefix $nameAndIPArray[1][$i] -SourcePortRange * -DestinationAddressPrefix * -DestinationPortRange 443
Set-AzureRmNetworkSecurityGroup -NetworkSecurityGroup $nsg
$priority = $priority + 10
}
Replace correct value to your script.
I assume this is the line that is empty, so you are not getting any vms back:
$vm = Get-AzureRmVM -ResourceGroupName $rgname -Name $vmname
so, check the $vm variable and if some vm's exist with those parameters.

Adding column to SQL query on multiple instances from powershell

I have the following powershell script which reads in a list of servers, and runs SQL command on these servers. This data is then exported to csv and to excel format
I would like to be able to add the targeted server name from my server list as the first column so columns would look like this (server name added to front)
Server Name | Name | CollectionSet ID | Collection Mode | Retention Period | Schedule
This is the current script I have:
Param
(
[string]$fServers = 'W:\Theo\Scripts\mdw_servers.csv'
)
$query = "SELECT a.name AS 'DC Name',
collection_set_id AS 'Collection_set ID',
CASE collection_mode
WHEN 1 THEN 'non-cached'
WHEN 0 THEN 'cached'
END AS 'Collection Type' ,
days_until_expiration AS 'Retention Period' ,
b.name AS 'Schedule Name'
FROM msdb.dbo.syscollector_collection_sets a ,
msdb.dbo.sysschedules b
WHERE a.schedule_uid = b.schedule_uid
AND is_running = 1;"
$csvFilePath = "W:\Theo\Scripts\queryresults.csv"
$excelFilePath = "W:\Theo\Scripts\queryresults.xls"
# Run Query against multiple servers, combine results
$allServers = Get-Content -Path $fServers
foreach ($Server in $allServers) {
write-host "Executing query against server: " $Server
$results += Invoke-Sqlcmd -Query $query -ServerInstance $Server;
}
# Output to CSV
write-host "Saving Query Results in CSV format..."
$results | export-csv $csvFilePath -NoTypeInformation
# Convert CSV file to Excel
write-host "Converting CSV output to Excel..."
$excel = New-Object -ComObject excel.application
$excel.visible = $False
$excel.displayalerts=$False
$workbook = $excel.Workbooks.Open($csvFilePath)
$workSheet = $workbook.worksheets.Item(1)
$resize = $workSheet.UsedRange
$resize.EntireColumn.AutoFit() | Out-Null
$xlExcel8 = 56
$workbook.SaveAs($excelFilePath,$xlExcel8)
$workbook.Close()
$excel.quit()
$excel = $null
write-host "Results are saved in Excel file: " $excelFilePath
Any input is appreciated!
have you tried
SELECT ##SERVERNAME AS 'Server Name'
https://msdn.microsoft.com/en-us/library/ms187944.aspx

Resources