Granting SQL Server role in a remote server - sql

I am working on an activity where I have a table (t1) in server1 which stores servername, userid and role information.
I created a PowerShell script to run on server1, collect these information from the table, connect to the remote server(servername) and grant the requested role to the userid.
Please find the script below:
$SQLServer = "Server1"
$SQLDatabase = "Db1"
$user = "select userid from T1"
$role = "select access from T1"
$server1 = "Select servername from T1"
<#store the table information in a text file for auditing#>
$Sqlquery = "Select servername,userid,access from T1"
$Test = Invoke-SQLCMD -Query $SqlQuery -serverinstance $SQLServer -Database
$SQLDatabase
$Test | format-list servername,userid,access| out-file "G:\EP\test.txt"
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
$SqlConnection.ConnectionString = "Server = $Server1; Integrated Security =
True"
$SqlCmd = New-Object System.Data.SqlClient.SqlCommand
$SqlCmd.CommandText = $SqlQuery
$SqlCmd.Connection = $SqlConnection
$SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
$SqlAdapter.SelectCommand = $SqlCmd
$sqlSrv = New-Object 'Microsoft.SqlServer.Management.Smo.Server'
"SQLServer\Instance"
foreach ($login in $user)
{
$login.AddToRole($role)
}
$SqlConnection.Close()
While running the above code, I see the following error:
Method invocation failed because [System.String] does not contain a method named 'AddToRole'.

Where is the $User in foreach ($login in $user) defined ?
Its null and foreach iterates to null (expexted behaviour) and $login becomes nothing here.
You might have missed setting $user here.

Related

Powershell connect to several INSTANCES of SQL Server

I need to create a script that do:
Get services where name are like MSSQL$* (in order to get all SQL Server instances services)
If the service are running, goes to check name
If the name of the service is like MSSQL$MICROSOFT##WID, throw a System.Exception
If not, try to connect to every instances running on the server, if one of them are inaccessible, throw a System.Exception
My current script:
$ErrorActionPreference = "Stop"
try {
$running = Get-Service | where {($_.Status -eq "Running") -and $_.Name -like "MSSQL*"}
$services = (Get-Service -Name 'MSSQL*')
$running.Status | Foreach-object {
if ($running.status -contains 'Running'){
if ($_.Name -ne 'MSSQL$MICROSOFT##WID'){
$instances = (get-itemproperty 'HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server').InstalledInstances
foreach ($instance in $instances){
$server = "localhost"
$database = "master"
$sql = "select name from sys.databases"
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
$SqlConnection.ConnectionString = "Server=$server;Database=$database;Integrated Security=SSPI"
$SqlConnection.Open()
$SqlCmd = New-Object System.Data.SqlClient.SqlCommand
$SqlCmd.CommandText = $sql
$SqlCmd.Connection = $SqlConnection
$SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
$SqlAdapter.SelectCommand = $SqlCmd
$DataSet = New-Object System.Data.DataSet
$SqlAdapter.Fill($DataSet)
$SqlConnection.Close()
$DataSet.Tables[0]
$return = ($DataSet.Tables[0])
if ($return -ne $null){return 1} else {[System.Exception]}
}}
} else {[System.Exception]}
}}catch{[System.Exception]}
finally{$ErrorActionPreference = "Stop"}
Check status of services with MSSQL*
If status of services is running, check name of service, if the name of service is equal to MSSQL$MICROSOFT##WID throw a System.Exception, if not, list all instances and then, foreach instance in instances, try to connect
If the connection is successful, return 1, if not, throw System.Exception
My questions are: with this script, doesn't matter how many instances have already installed in the server, only check the default... how can I make try the connection for every instance that is running?
I need to check the connection to each instance running on the server, if there is someone stopped, or inaccessible must throw a System.Exception.
And too, is not possible use dbatools, and invokesql...
Somebody knows how to make the connection to every instances running on the server, if one of them are inaccessible, throw a System.Exception?
------------------- UPDATED ------------------------
this string succesfully connect with an example of instance
$SqlConnection.ConnectionString = "Server='localhost\NEWINSTANCE';Database=master;Integrated Security=SSPI"
I just try to send the server trough variable, but give me error...
if ($_.Name -ne 'MSSQL$MICROSOFT##WID'){
$SQLinstancesold = dir "SQLSERVER:\SQL\(local)"
$SQLinstances = $SQLinstancesold | Format-table -HideTableHeaders
foreach ($SQLinstance in $SQLInstances) {
$server = 'localhost\'+$SQLinstance;
$database = "master"
$sql = "select name from sys.databases"
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
$SqlConnection.ConnectionString = "Server='localhost\NEWINSTANCE';Database=master;Integrated Security=SSPI"
$SqlConnection.Open()
$SqlCmd = New-Object System.Data.SqlClient.SqlCommand
$SqlCmd.CommandText = $sql
$SqlCmd.Connection = $SqlConnection
$SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
$SqlAdapter.SelectCommand = $SqlCmd
$DataSet = New-Object System.Data.DataSet
$SqlAdapter.Fill($DataSet)
$SqlConnection.Close()
$DataSet.Tables[0]
$return = ($DataSet.Tables[0])
if ($return -ne $null){return 1} else {echo "else of return 1"}
}}
I dont know how can I pass the variable to connectionstring in order to do the connection for each instance
--------------------------- UPDATED 2 -----------------------
I just modified the script:
Import-Module SQLPS -DisableNameChecking
$ErrorActionPreference = "Stop"
try {
$running = Get-Service | where {($_.Status -eq "Running") -and $_.Name -like "MSSQL*"}
$services = (Get-Service -Name 'MSSQL*')
$running.Status | Foreach-object {
if ($running.status -contains 'Running'){
if ($_.Name -ne 'MSSQL$MICROSOFT##WID'){
$SQLinstancesold = dir "SQLSERVER:\SQL\(local)"
$SQLinstances = $SQLinstancesold | Format-table -HideTableHeaders
$server = 'localhost\'+$SQLinstance;
foreach ($SQLinstance in $SQLInstances) {
$server = 'localhost\'+$SQLinstance;
$return = Invoke-Sqlcmd -ServerInstance $server -Database master -Query "select name from sys.databases"
if ($return -ne $null){return 1} else {echo "else of return 1"}
}}
} else {echo "here"}
}
}
catch{echo "hello?"}
finally{$ErrorActionPreference = "Stop"}
This way, list all instances, and for each instance declare variable server and try to invoke-sqlcmd
But, when I try to invoke $server on $return variable, give me an error, if I write for example the name of my instance NEWINSTANCE, return 1, that is correct
How can I put variable $server in order to get all instances on invokesqlcmd?

How to use powershell to obtain the data from excel and select from SQL

I have an excel file that contain a list of staff information,
now I want to use the column Staff_ID in xlsx to select those Staff_ID from sql table and get the result by export_CSV, so I try to write a powershell that could be run in schedule task.
How should I get the information from excel and select those data from sql table?
$SQLServer = "ServerName"
$SQLDBName = "DBName"
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
$SqlConnection.ConnectionString = "Server = $SQLServer; Database =
$SQLDBName; User ID= YourUserID; Password= YourPassword"
$SqlCmd = New-Object System.Data.SqlClient.SqlCommand
$SqlCmd.CommandText = 'StoredProcName'
$SqlCmd.Connection = $SqlConnection
$SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
$SqlAdapter.SelectCommand = $SqlCmd
$DataSet = New-Object System.Data.DataSet
$SqlAdapter.Fill($DataSet)
$SqlConnection.Close()
$gSqlAuthMode = True
SELECT * FROM [DBName].[dbo].[DETAILS] WHERE [STATUS] not like 'Inactive-%' ORDER BY NUMBER ASC
$List = Import-XLSX -Path "D:\Stafflist.xlsx"
foreach ($item in $List){
If ((?? -eq $item.StaffCuid.toUpper().trim())) {
$Report = [PSCustomObject] #{
}
}
}
$Report | Export-Csv D:\report.csv -Delimiter "," -NoTypeInformation -append
}
You could use Import-Excel cmdlet from the famous ImportExcel module and Invoke-SQLCmd (part of SQL management tools)
$Excel = Import-Excel -Path D:\Stafflist.xlsx
Foreach($Row in $Excel) {
$StaffID = $Row.StaffCuid.toUpper().trim()
$Query = "create your query here with $StaffId"
Invoke-SQLCmd -Query $Query -ServerInstance "$ServerName\$DBName"
}

Cyclic csv-Import to SQL file by file

I try to write a short script that cyclic imports csv-files to a SQL data table that are being dropped in an import folder ($sourceSQL). Each csv-file consists of one line of information (here three columns). To make sure that the file was successfully written to the data table, I check if the unique Id can be found in the table.
So far it works with the first file. However, the second file gets only moved to the destination folder without being written to the data table. I can't find the problem. Is it because of the $data variable?
$StartButton.Add_Click({
$script:ActiveLoop = $true
while ($script:ActiveLoop){
If (Test-Path $sourceSQL){
$data = $null
Do{
$data = import-csv $impcsv -Header A,B,C
foreach($i in $data)
{
$Id = $i.A
$State = $i.B
$Sequence = $i.C
$query = INSERT INTO $SQLTable (Id, State, Sequence)
VALUES ('$Id','$State','$Sequence')"
$impcsv = invoke-sqlcmd -Database $SQLDatabase -Query $query -serverinstance $SQLInstance -Username $SQLUsername -Password $SQLPassword}
$SqlQueryId = "SELECT TOP 1 Id from $SQLTable ORDER BY Id DESC"
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
$SqlConnection.ConnectionString = "Server = $SQLInstance; Database = $SQLDatabase; User ID = $SQLUsername; Password = $SQLPassword"
$SqlCmd = New-Object System.Data.SqlClient.SqlCommand
$SqlCmd.CommandText = $SqlQueryId
$SqlCmd.Connection = $SqlConnection
$SqlConnection.Open()
$IdCheck= [string]$SqlCmd.ExecuteScalar()
$SqlConnection.Close()
} Until ($Id -eq $IdCheck)
Move-Item $sourceSQL -Destination $destinationSQL
}
[System.Windows.Forms.Application]::DoEvents()
}
})
$objForm.Controls.Add($StartButton)

Powershell import csv to SQL queries, export multiple csvs

I'm struggling to get my head around this one, I've only just begun looking at scripting in SQL, and my powershell is very limited. The requirments are basically this:
Utilisng Powershell, import a csv file which contains one column that needs to feed into multiple SQL queries via a loop, exporting a seperate csv file for each different query.
example import of csv:
Project (heading)
1000
1001
1002
Powershell:
$importProjectsCSV = e:\Projects.csv
$server = servername
$database = database
import-csv $importProjectsCSV | ForEach-Object {
$query = "
Select ProjectLeader, ProjectTitle
FROM dbo.PROJECTS
Where Project = $_.Project;
Select ProjectClient, Name
FROM dbo.CLIENTS
Where Project = $_.Project;
$connectionTemplate = "Data Source={0};Integrated Security=SSPI;Initial Catalog={1};"
$connectionString = [string]::Format($connectionTemplate, $server, $database)
$connection = New-Object System.Data.SqlClient.SqlConnection
$connection.ConnectionString = $connectionString
$command = New-Object System.Data.SqlClient.SqlCommand
$command.CommandText = $query
$command.Connection = $connection
$SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
$SqlAdapter.SelectCommand = $command
$DataSet = New-Object System.Data.DataSet
$SqlAdapter.Fill($DataSet)
$connection.Close()
$dataset.Table[0] | Export-csv "E:\" + $_.ProjectName + ".csv"
$dataset.Table[1] | Export-csv "E:\" + $_.ProjectName + ".csv"
The problem is that the variable isn't coming into the SQL query.
Is there a better way to handle this type of example?
Appreciate any pointers
Paul.
I would do something like this:
I must admit i havent been able to test it, and personally i usually use c# to query sql servers. So i might have gone a bit wrong somewhere.
$importProjectsCSV = e:\Projects.csv
$server = servername
$database = database
$Projects = Import-Csv -Path $importProjectsCSV | % {$_.Project}
$DS_Projects = New-Object System.Data.DataSet
$DS_Clients = New-Object System.Data.DataSet
$query_pro = "Select ProjectLeader, ProjectTitle, Project FROM dbo.PROJECTS";
$query_Clients = "Select ProjectClient, Name, Project FROM dbo.CLIENTS";
$connectionTemplate = "Data Source={0};Integrated Security=SSPI;Initial Catalog={1};"
$connectionString = [string]::Format($connectionTemplate, $server, $database)
$connection = New-Object System.Data.SqlClient.SqlConnection
$connection.ConnectionString = $connectionString
$connection.Open()
$command = $connection.CreateCommand();
$command.CommandText = $query_pro;
$sqlAdap = New-Object System.Data.SqlClient.SqlDataAdapter($command)
$sqlAdap.Fill($DS_Projects)
$command2 = $connection.CreateCommand();
$command2.CommandText = $query_Clients;
$sqlAdap2 = New-Object System.Data.SqlClient.SqlDataAdapter($command2)
$sqlAdap2.Fill($DS_Clients)
$connection.Close();
foreach($project in $Projects)
{
$DS_Projects.Tables[0].Select("Project=$project") | Export-Csv "E:\$project.csv"
$DS_Clients.Tables[0].Select("Project=$project") | Export-Csv "E:\$project.csv"
}
If I get every right you could do this with something like this:
$Projects = Import-Csv -Path 'C:\Temp\Projects.csv'
ForEach ($Project in $Projects.Projects) {
$Query= #"
Select ProjectLeader, ProjectTitle
FROM dbo.PROJECTS
Where Project = '%{0}';
"# -f $Project
# just dummy action
$results = Invoke-Sqlcmd -ServerInstance "foo" -Database "bar" -Query $Query
# In Case results is a dataset do something like
$results | Export-Csv -Path ("E:\{0}.csv" -f $Project )
}

Two SQL queries and pass each as a dataset to pass as parameters

Im very new to Powershell and I've scraped together the following code for a script that will check for an 'old' folder and create it if not found. It will then move the compressed weblogs from current location to the 'old' folder. I am wanting this written to pull the server names and website names from a sql query so it can be setup to run nightly and does not need to be updated with new or deleted servers. I have the following written so far but since I'm new I cannot figure out the last bit of syntax.
clear
$SqlServer = "SERVER"
$SqlCatalog = "DATABASE"
$SqlQuery = "select hsa.servername from SERVER.dbo.serversapp hsa
inner join SERVER.dbo.apphosts hah on hsa.vchservername = hah.vchservername
where hsa.tirecordstatus = 1
order by hsa.vchservername desc"
$SqlQuery1 = "select hah.vchhost from SERVER.dbo.serversapp hsa
inner join SERVER.dbo.apphosts hah on hsa.vchservername = hah.vchservername
where hsa.tirecordstatus = 1
order by hsa.vchservername desc"
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
$SqlConnection.ConnectionString = "Server = $SqlServer; Database = $SqlCatalog; Integrated Security = True"
$SqlCmd = New-Object System.Data.SqlClient.SqlCommand
$SqlCmd.CommandText = $SqlQuery
$SqlCmd.Connection = $SqlConnection
$SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
$SqlAdapter.SelectCommand = $SqlCmd
$DataSet = New-Object System.Data.DataSet
$SqlAdapter.Fill($DataSet)
$SqlConnection.Close()
$DataSet.Tables[0]
What I would like to do is have each $SQLQuery run and store each one in it's own dataset table so I can pass each one as a parameter for the following code. What I need to figure out is:
1)How do I write the above to run both SQL queries and have each one be either it's own dataset or table so I can them as parameters; $Servername and $HostedGroup?
2)How do I set this up to recurse the below code on each of the servers from the $Servername parameter?
$Servername = Dataset1
$Hostedgroup = Dataset2
$OldFolder = "\\$Servername\C$\Ren\Weblogs\$Hostedgroup\old"
$FolderExists = Test-Path $OldFolder
if($FolderExists -eq $False)
{
new-item \\$Servername\C$\Ren\Weblogs\$Hostedgroup\old -type directory
}
then
{
if(Test-Path \\$Servername\C$\Ren\Weblogs\old\W3SVC2)
{
get-childitem -path '\\$Servername\C$\Ren\WebLogs\$Hostedgroup\W3SVC2' -recurse -include *.zip | move-item -destination '\\$Servername\C$\Ren\WebLogs\$Hostedgroup\old'
$SqlAdapter.Fill($DataSet) returns an integer; you should prevent your script/function from returning it by assigning it to $null or using Out-Null. (e.g. $null = $SqlAdapter.Fill($DataSet))
For the answer to your question, you can return both fields from a single query, then iterate over the results. The pattern would look like this:
function Get-HostedServerApp {
$SqlServer = "SERVER"
$SqlCatalog = "DATABASE"
$SqlQuery = #"
select hsa.vchappservername, hah.vchhost
from SERVER.dbo.hostedserversapp hsa
inner join SERVER.dbo.hostedapphosts hah on hsa.vchappservername = hah.vchappservername
where hsa.tirecordstatus = 1
order by hsa.vchappservername desc
"#
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
$SqlConnection.ConnectionString = "Server = $SqlServer; Database = $SqlCatalog; Integrated Security = True"
$SqlCmd = New-Object System.Data.SqlClient.SqlCommand
$SqlCmd.CommandText = $SqlQuery
$SqlCmd.Connection = $SqlConnection
$SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
$SqlAdapter.SelectCommand = $SqlCmd
$DataSet = New-Object System.Data.DataSet
$null = $SqlAdapter.Fill($DataSet)
$SqlConnection.Close()
$Dataset.Tables[0]
}
# Place your logic in this function
function SomeFunction {
param(
$Servername,
$Hostedgroup
)
"\\$Servername\C`$\Renaissance\Weblogs\$Hostedgroup\old"
}
$data = Get-HostedServerApp
$data| foreach{SomeFunction -ServerName $_.vchappservername -HostedGroup $_.vchhost}