J'essaie d'obtenir un rapport sur l'espace disque de bouquet de serveurs et d'insérer-les dans une table SQL.
vous trouverez ci-dessous un échantillon de ce que j'essaie de faire, les noms de serveurs seront remplis à partir d'une table et transmis comme une liste séparée de virgule.Even Passing One Server a également le même résultat.
set @ps = 'powershell.exe -noexit -c "
$computers=Get-WmiObject -Class Win32_Volume '+@servernames+'
foreach($computer in $computers)
{
$pscomputername=$computer.pscomputername
$name=$computer.name
$capacity=$computer.capacity
$freespace=$computer.freespace
$Label=$computer.Label
$insertquery="
INSERT INTO [dbo].[temp_disksdata]
(
[servername] ,
[DiskName] ,
[Capacity(GB)] ,
[FreeSpace(GB)],
[label]
)
VALUES
(''$pscomputername'' ,
''$name'' ,
''$capacity'',
''$freespace'',
''$Label''
)
GO
"
Invoke-SQLcmd -query $insertquery -ServerInstance ''someserver'' -Database dbname
}
"';
Maintenant, j'essaie de passer la variable à xp_cmdshell comme ci-dessous
execute xp_cmdshell @ps;
Lorsqu'il est invoqué dans SSMS, ci-dessus renvoie NULL, mais fonctionne dans PowerShell ..
Des idées pourquoi?
Vous trouverez ci-dessous quelques points que j'ai essayés
1.Same compte (admin) est utilisé dans la coque et les SSMS
2. a été multiplié de choses comme des modules d'importation
3.xp_cmdshell fonctionne, mais cela renvoie NULL uniquement pour cette requête
[.____] 4.J'ai essayé d'ajouter -nowait, mais cela n'aide pas aussi bien
J'ai essayé de faire cela de plus d'une journée, mais cela ne fonctionne pas. Je dois utiliser XP_CMDSHELL parce que la rédaction de l'application AC # n'est pas autorisée.Bat Fichier ne vous aide pas car les noms de serveur sont transmis comme une virgule liste séparée.
Je modifie le code et on m'a dit de ne pas réécrire lorsqu'il est demandé
S'il vous plaît laissez-moi savoir si vous avez besoin d'autres informations
repro :
[.____] ci-dessous est une commande complète de l'impression, si vous supprimez PowerShell.exe et -C, ci-dessous s'exécutera à PowerShell.
powershell.exe -c "
$computers=Get-WmiObject -Class Win32_Volume 'servername'
foreach($computer in $computers)
{
$pscomputername=$computer.pscomputername
$name=$computer.name
$capacity=$computer.capacity
$freespace=$computer.freespace
$Label=$computer.Label
$insertquery="
INSERT INTO [dbo].[temp_disksdata]
(
[servername] ,
[DiskName] ,
[Capacity(GB)] ,
[FreeSpace(GB)],
[label]
)
VALUES
('$pscomputername' ,
'$name' ,
'$capacity',
'$freespace',
'$Label'
)
"
Invoke-SQLcmd -query $insertquery -ServerInstance 'servername' -Database dbname
}
"
Il y a plusieurs problèmes ici:
Le problème principal est que la CMD Shell exécute chaque ligne lorsqu'il s'agit d'un retour; Cela n'attend pas un indicateur "fin de commande" tel que ;
, il tente pas non plus de le comprendre via une analyse (comme SQL le fait sur un lot étant soumis) car il n'y a aucun moyen de savoir si un return termine une "commande" ou non. Généralement, vous pouvez utiliser ^
Pour la poursuite de la ligne, mais cela ne semble pas fonctionner dans un paramètre d'entrée (au moins pas un avec une citation non excliquée).
Donc, pour cela, vous devez tout réduire à une seule ligne. C'est assez simple car vous pouvez simplement remplacer le retour du chariot (caractère 13) avec une chaîne vide et une ligne de ligne/nouvelle ligne (caractère 10) avec un espace. Mais cela seul ne fonctionnera pas depuis que les déclarations de Powershell doivent être séparées par une nouvelle ligne o un point-virgule. Les nouvelles lignes sont actuellement utilisées, mais si nous remplaçons ceux avec des espaces, les points-virgules seront nécessaires.
Par conséquent, l'étape 1 est d'ajouter un point-virgule à chaque commande réelle PowerShell. Ceci est probablement de bonne forme en général, comme avec T-SQL.
L'étape 2 ferait les deux REPLACE
appels contre le @ps
variable pour la transformer en un script à une seule ligne.
Vous avez des guillemets intégrées, utilisées pour créer la déclaration INSERT
. Ceux-ci doivent être échappés avec une barre oblique inverse (\
).
Get-WmiObject
ne semble pas fonctionner comme vous essayez de l'utiliser ici. Je devais ajouter -ComputerName
Pour passer au nom d'un système. Si je l'ai laissé vide, il a renvoyé les informations système locales, mais si vous souhaitez renvoyer des informations sur des serveurs distants, vous devrez probablement utiliser le -ComputerName
Commutatez et passez dans un serveur à la fois. Cela nécessitera probablement une boucle extérieure supplémentaire pour gérer plusieurs noms de serveurs (et ce serait là que vous passez dans @ServerNames
Pour se faire scinder pour itération à travers la liste).
Sont "Capacité" et "Freepace" vraiment stocké en tant que chaînes dans la DB? Sinon, vous voudrez peut-être supprimer les citations simples autour de ces "valeurs" dans la déclaration INSERT
.
Les suivants au moins exécutes. Vous pouvez déboguer de là. Je reçois des erreurs de ne pas trouver le module "Invoke-SQLCMD" ou quelque chose.
DECLARE @ServerNames NVARCHAR(4000);
SET @ServerNames = N'ALBRIGHT';
DECLARE @ps NVARCHAR(4000);
SET @ps = N'powershell.exe -noexit -c "
$computers=Get-WmiObject -Class Win32_Volume -ComputerName ' + @ServerNames + N';
foreach($computer in $computers)
{
$pscomputername=$computer.pscomputername;
$name=$computer.name;
$capacity=$computer.capacity;
$freespace=$computer.freespace;
$Label=$computer.Label;
$insertquery=\";
INSERT INTO [dbo].[temp_disksdata]
(
[servername] ,
[DiskName] ,
[Capacity(GB)] ,
[FreeSpace(GB)],
[label]
)
VALUES
(''$pscomputername'' ,
''$name'' ,
''$capacity'',
''$freespace'',
''$Label''
)
GO
\";
Invoke-SQLcmd -query $insertquery -ServerInstance ''someserver'' -Database dbname;
}
"';
SET @ps = REPLACE(REPLACE(@ps, NCHAR(13), N''), NCHAR(10), N' ')
PRINT @ps;
EXEC xp_cmdshell @ps;
Le problème peut être la ligne d'alimentation dans la commande que vous essayez d'exécuter. Considérez la différence entre les cas suivants.
-- case 1
execute xp_cmdshell N'powershell.exe -noexit -c "echo hello"';
-- case 2
execute xp_cmdshell N'powershell.exe -noexit -c "
echo hello
"';
Exécuter multil-ligne powershell
scripts de xp_cmdshell
, essayez d'enregistrer la commande entière à un fichier, puis de référencer le fichier dans un appel unique. Par exemple, étant donné ce texte ...
$srvs =@(
'localhost',
'127.0.0.1',
'(local)'
)
$srvs | % {
(Invoke-SqlCmd -ServerInstance "$_" -Query 'select @@servername srv').srv
}
... enregistré comme fichier C:\temp\foo.ps1
, ce qui suit devrait réussir ...
execute xp_cmdshell N'powershell.exe c:\temp\foo.ps1';
Il existe un certain nombre de moyens d'écrire un texte arbitraire dans le système de fichiers à l'aide de T-SQL, mais la lecture plus intelligente est susceptible d'avoir un fichier statique et d'obtenir le @servernames
D'un autre appel à la DB - par exemple, changer cela ...
$computers = Get-WmiObject -Class Win32_Volume '+@servernames+'
... dans un script SQL à cette ...
$conn = @{
ServerInstance = $server
Database = $database
}
$servernames = (Invoke-SqlCmd @conn -query "select Name from server_list").Name -join ','
$computers = Get-WmiObject -Class Win32_Volume '$servernames'
... dans un script PowerShell.
Fortement recommandé d'utiliser un module génial dbatools PowerShell.
Il suffit d'installer ce module:
Install-Module dbatools
Ensuite, utilisez get-dbadatabasespace Fonction:
# Get Db Free Space AND write it to table
Get-DbaDatabaseSpace -SqlInstance $instance | Out-GridView
Get-DbaDatabaseSpace -SqlInstance $instance -IncludeSystemDB | Out-DbaDataTable | Write-DbaDataTable -SqlInstance $instance -Database tempdb -Table DiskSpaceExample -AutoCreateTable
Invoke-Sqlcmd2 -ServerInstance $instance -Database tempdb -Query 'SELECT * FROM dbo.DiskSpaceExample' | Out-GridView
Ou pour multiplier des instances:
$allservers = "localhost\sql2016", "localhost\sql2017"
$allservers | Get-DbaDatabaseSpace -IncludeSystemDB | Out-DbaDataTable | Write-DbaDataTable -SqlInstance $instance -Database tempdb -Table DiskSpaceExample -AutoCreateTable
Vous pouvez également utiliser un modèle PowerBI très utile pour cette tâche: https://sqljana.wordpress.com/2018/04/30/sql-server-quick-space-file-layout-Analysis-vith-powershell- and-powerbi /