Melhorando o “Abrir o prompt de comando aqui”


Não sei se é de conhecimento de todos mas, já faz alguns anos que você pode usar o SHIFT + Botão Direito e vai aparecer uma opção de “Abrir o prompt de comando aqui” e ele vai abrir uma tela do DOS dentro daquela estrutura de diretório.

A mesma coisa vale para qualquer parte vazia na janela de conteúdo.

Isso ajuda? ajuda, mas tem um detalhe chato, ele não abre o prompt elevado e mesmo nas versões mais recentes não tem a opção de powershell. Fora que para abrir tem que pressionar o SHIFT junto.

É possível melhorar isso? claro, senão não estaria escrevendo este post…

Só testei no Windows 10, se alguém testar em outras versões deixa um comentário dizendo se funcionou ou não e em qual versão.

Para isso vamos ter que adicionar algumas linhas de registro.

Sempre vale um ATENÇÃO !!! Se você não sabe alterar, ou tem medinho, de alterar o registro clique aqui.

Para os outros, abaixo tem o que vocês precisam copiar e salvar em um arquivo .reg, após importar o arquivo vocês terão 2 novos menus com o Botão Direito:


Windows Registry Editor Version 5.00

; Command Prompt

[HKEY_CLASSES_ROOT\Directory\shell\01MenuCmd]
"MUIVerb"="Command Prompts"
"Icon"="cmd.exe"
"ExtendedSubCommandsKey"="Directory\\ContextMenus\\MenuCmd"

[HKEY_CLASSES_ROOT\Directory\background\shell\01MenuCmd]
"MUIVerb"="Command Prompts"
"Icon"="cmd.exe"
"ExtendedSubCommandsKey"="Directory\\ContextMenus\\MenuCmd"

[HKEY_CLASSES_ROOT\Directory\ContextMenus\MenuCmd\shell\open]
"MUIVerb"="Command Prompt"
"Icon"="cmd.exe"

[HKEY_CLASSES_ROOT\Directory\ContextMenus\MenuCmd\shell\open\command]
@="cmd.exe /s /k pushd \"%V\""

[HKEY_CLASSES_ROOT\Directory\ContextMenus\MenuCmd\shell\runas]
"MUIVerb"="Command Prompt Elevated"
"Icon"="cmd.exe"
"HasLUAShield"=""

[HKEY_CLASSES_ROOT\Directory\ContextMenus\MenuCmd\shell\runas\command]
@="cmd.exe /s /k pushd \"%V\""
; PowerShell

[HKEY_CLASSES_ROOT\Directory\shell\02MenuPowerShell]
"MUIVerb"="PowerShell Prompts"
"Icon"="powershell.exe"
"ExtendedSubCommandsKey"="Directory\\ContextMenus\\MenuPowerShell"

[HKEY_CLASSES_ROOT\Directory\background\shell\02MenuPowerShell]
"MUIVerb"="PowerShell Prompts"
"Icon"="powershell.exe"
"ExtendedSubCommandsKey"="Directory\\ContextMenus\\MenuPowerShell"

[HKEY_CLASSES_ROOT\Directory\ContextMenus\MenuPowerShell\shell\open]
"MUIVerb"="PowerShell"
"Icon"="powershell.exe"

[HKEY_CLASSES_ROOT\Directory\ContextMenus\MenuPowerShell\shell\open\command]
@="powershell.exe -noexit -command Set-Location '%V'"

[HKEY_CLASSES_ROOT\Directory\ContextMenus\MenuPowerShell\shell\runas]
"MUIVerb"="PowerShell Elevated"
"Icon"="powershell.exe"
"HasLUAShield"=""

[HKEY_CLASSES_ROOT\Directory\ContextMenus\MenuPowerShell\shell\runas\command]
@="powershell.exe -noexit -command Set-Location '%V'"
; Ensure OS Entries are on the Extended Menu (Shift-Right Click)

[HKEY_CLASSES_ROOT\Directory\shell\cmd]
"Extended"=""

[HKEY_CLASSES_ROOT\Directory\background\shell\cmd]
"Extended"=""

[HKEY_CLASSES_ROOT\Directory\shell\Powershell]
"Extended"=""

[HKEY_CLASSES_ROOT\Directory\background\shell\Powershell]
"Extended"=""

Anúncios

PowerShell e o Metrô


Trabalhando no centro você acaba tendo que usar muito o transporte público, o que na maioria dos casos é muito chato,,,

Devido a chuva a operação da CPTM e do Metrô estavam com algumas lentidões, mas nada comparado ao sites deles,,,

O site do Metrô (www.metro.sp.gov.br) estava muito lento, um tempo de resposta de uns 10/15 segundos.

O da CPTM (www.cptm.sp.gov.br) não estava muito longe disso também,,,

Aí fiquei pensando se algum deles tinha uma API para trazer a informação do status da linha e descobri que, claro, nenhum deles tem isso…

Mas, a Viaquatro, que opera a linha 4 do metrô tem uma API, que apenas trás as informações do metrô e por curiosidade não trás informações sobre a própria linha 4,,, mas já está valendo….

O site com as informações das linhas de metrô é o: http://www.viaquatro.com.br/generic/Main/LineStatus

Legal, não precisa de chave de API, não tem necessidade de autenticação, é bem simples e direto…

metro

 

Agora com isso já é possível trabalhar um pouco com o poweshell…


$metro = Invoke-RestMethod -Uri "http://www.viaquatro.com.br/generic/Main/LineStatus" | select * -ExpandProperty StatusMetro
$linha = $metro.ListLineStatus

$linha | select Line,StatusMetro

E agora tenho uma pesquisa direta do status das linhas na hora que eu quiser e sem ter que abrir o site do metrô,,,

Quando eu descobrir se a CPTM tem o mesmo tipo de serviço tento incorporar no código,,,

 

Coisas que só o Powershell ISE faz por você


Tenho alguns clusters que estão em Multi-site, usando um Quorum em File Share para melhorar a disponibilidade.

Como o “Cluster Core” conta como voto na contagem do cluster, tenho preferencia na localização do recurso, já que todos os nós estão votando ele trabalha como voto de desempate.

Eu precisava monitorar um evento bem particular do cluster, quando o “Cluster Core Resource” alterasse de site ele deveria alarmar pelo SCOM, até ai tranquilo,,, faço um script no POSH o pessoal coloca para monitorar e pronto….


$API = new-object -comObject "MOM.ScriptAPI"
$bag = $api.CreatePropertyBag()
$resultado = (@(Get-ClusterGroup "Cluster Group" | Where-Object {$_.OwnerNode -like 'SERVIDOR*'}).count -eq 1)

if ($resultado -eq $true)
{
$bag.AddValue("State","Good")}
else
{
$bag.AddValue("State","Bad")}

$API.Return($bag)

Enquanto testava o script percebi que o Powershell ISE estava retornando mensagem de erro, fiquei revendo argumento, variável, comObject, mas não encontrei o erro…

Powershell ISE SCOM Script Error

Powershell ISE SCOM Script Error

Exception calling “Return” with “1” argument(s): “The handle is invalid. (Exception from HRESULT: 0x80070006 (E_HANDLE))”
At line:15 char:1
+ $API.Return($bag)
+ ~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : ComMethodTargetInvocation

A solução é bem simples:

  • Salve o arquivo em script e execute ele… o ISE não consegue interpretar o retorno do comObject.

O retorno é alguma coisa como:

<DataItem type=”System.PropertyBagData” time=”2015-06-24T12:18:40.1270337-03:00″ sourceHealthServiceId=”68A1F050-F975-9EE7-E0F3-C2CDE3445FD3″><Property Name=”State” VariantType=”8″>Bad</Property></DataItem>

Gerar DACPAC usando Powershell v2


Para quem leu meu post anterior de Gerar DACPAC usando Powershell percebeu que é um código muito simples, passível de erros como:

  • digitar o nome do banco errado,
  • símbolos nos nomes dos bancos,
  • não escolher um local ou o nome do servidor ele gera erro no script…

coisas bem primárias…

Dessa vez fiz algumas melhorias,,, como:

  • Tela mais simpática
  • conecta no servidor de banco e lista as bases
  • multi seleção de bases
  • não deixa você continuar se não selecionar um destino e colocar o nome do servidor
  • Não fecha sozinho
  • deixa na tela do posh o código para, caso precise, você copie e execute novamente
  • coloca o mouse como ampulheta
  • abre o diretório de destino quando acaba a tarefa
  • e localiza o executável do gerador de dacpac,,, desde que esteja no C:\

Uma coisa muito importante, o código é livre para modificação, sinta-se a vontade de fazer qualquer modificação que queira,, se achar que sua versão ficou melhor, ou achou algum problema e corrigiu, mande o código, vamos melhorar,,,,

Existe um detalhe para rodar esta versão sem problemas,,, você precisa ter o Windows atualizado além do Windows Management Framework 4.0 e do .Net Frame Work 4.5.2.

gerador de dacpac v2


cls
$data = get-date -format "_yyyyMMdd"
$WindowTitle = "Gerador de DACPAC"
$LabelPath = "Caminho:"
$LabelServer = "Servidor:"

$aplicacao = (Get-ChildItem -Path "C:\Program Files (x86)\Microsoft SQL Server" -Include "SqlPackage.exe" -Recurse | % { $_.FullName } | Select-Object -First 1)

$x = @()

[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")

$objForm = New-Object System.Windows.Forms.Form
$objForm.Text = $WindowTitle
$objForm.Size = New-Object System.Drawing.Size(290,270)
$objForm.StartPosition = "CenterScreen"
$objForm.FormBorderStyle = 'FixedSingle'

$objForm.KeyPreview = $True
$objForm.Add_KeyDown({if ($_.KeyCode -eq "Escape")
{$objForm.Close()}})

$OKButton = New-Object System.Windows.Forms.Button
$OKButton.Location = New-Object System.Drawing.Size(75,215)
$OKButton.Size = New-Object System.Drawing.Size(75,23)
$OKButton.Text = "OK"

$OKButton.Add_Click(
{$i=0
if ( $textbox2.Text -eq '' )
{

}

else
{
foreach ($objItem in $objListbox.SelectedItems)
{
#[System.Windows.Forms.Application]::UseWaitCursor=$true
$objForm.Cursor=[System.Windows.Forms.Cursors]::WaitCursor
$local = $textbox2.Text
$servidor = $textBoxServerName.Text
Write-Progress -Activity "Gerando DACPAC" -status "Gerando DACPAC para $objItem" -percentComplete ($i++ / ($objListbox.SelectedItems).Count*100)
$exec = ("'$aplicacao'" + " /a:Extract /ssn:"+$servidor +" /sdn:"+$objItem +" /tf:"+"'$local'"+"\"+$objItem+$data+".dacpac")
#$exec = (C:\"Program Files (x86)"\"Microsoft SQL Server"\110\DAC\bin\SqlPackage.exe /a:Extract /ssn:$servidor /sdn:$objItem /tf:$local\$objItem$data+.dacpac)
write-host $exec
Invoke-expression "& $exec"
#[System.Windows.Forms.Application]::UseWaitCursor=$false
$objForm.Cursor=[System.Windows.Forms.Cursors]::Default
}
#Invoke-Item "$local"
}
if ( $checkbox1.Checked -eq $true -and $textbox2.Text -ne '')
{
Invoke-Item "$local"
}

else
{

}

#$objForm.Close()
})

$objForm.Controls.Add($OKButton)

$CancelButton = New-Object System.Windows.Forms.Button
$CancelButton.Location = New-Object System.Drawing.Size(150,215)
$CancelButton.Size = New-Object System.Drawing.Size(75,23)
$CancelButton.Text = "Fechar"
$CancelButton.Add_Click({$objForm.Close()})
$objForm.Controls.Add($CancelButton)

# create your checkbox
$checkbox1 = new-object System.Windows.Forms.checkbox
$checkbox1.Location = new-object System.Drawing.Size(260,66)
$checkbox1.Size = new-object System.Drawing.Size(250,50)
$checkbox1.Checked = $true
$objForm.Controls.Add($checkbox1)

$objLabel = New-Object System.Windows.Forms.Label
$objLabel.Location = New-Object System.Drawing.Size(10,120)
$objLabel.Size = New-Object System.Drawing.Size(280,20)
$objLabel.Text = "Selecione a base que deseja extrair o DACPAC:"
$objForm.Controls.Add($objLabel)

$objListbox = New-Object System.Windows.Forms.Listbox
$objListbox.Location = New-Object System.Drawing.Size(10,140)
$objListbox.Size = New-Object System.Drawing.Size(260,20)
$objLabel2 = New-Object System.Windows.Forms.Label
$objLabel2.Location = New-Object System.Drawing.Size(10,10)
$objLabel2.Size = New-Object System.Drawing.Size(280,20)
$objLabel2.Text = "Servidor:"
$objForm.Controls.Add($objLabel2)

# Create textbox for the Server name
$textBoxServerName = New-Object System.Windows.Forms.TextBox
$textBoxServerName.DataBindings.DefaultDataSourceUpdateMode = 0
$textBoxServerName.Location = New-Object System.Drawing.Size(10,30)
$textBoxServerName.Name = "ServerName"
$textBoxServerName.Size = New-Object System.Drawing.Size(100,10)
$textBoxServerName.TabIndex = 0
$objForm.Controls.Add($textBoxServerName)
$objLabel2 = New-Object System.Windows.Forms.Label
$objLabel2.Location = New-Object System.Drawing.Size(10,60)
$objLabel2.Size = New-Object System.Drawing.Size(280,20)
$objLabel2.Text = "Destino:"
$objForm.Controls.Add($objLabel2)

# Create textbox used to file destination
$textBox2 = New-Object System.Windows.Forms.TextBox
$textBox2.DataBindings.DefaultDataSourceUpdateMode = 0
$textBox2.Location = New-Object System.Drawing.Size(10,80)
$textBox2.Name = "textBox2"
$textBox2.Size = New-Object System.Drawing.Size(150,10)
$textBox2.TabIndex = 0
$objForm.Controls.Add($textBox2)

$app = New-Object -ComObject Shell.Application
$destino_OnClick=
{
#TODO: Place custom script here

try {
$browseForFolderOptions = 0
if ($NoNewFolderButton) { $browseForFolderOptions += 512 }

$folder = $app.BrowseForFolder(0, $Message, $browseForFolderOptions, $InitialDirectory)
#if ($folder) { $selectedDirectory = $folder.Self.Path } else { $selectedDirectory = '' }
#[System.Runtime.Interopservices.Marshal]::ReleaseComObject($app) > $null
$textbox2.Text = $folder.Self.Path #$selectedDirectory #.FileName.ToString()

}
catch {
[System.Windows.Forms.MessageBox]::Show(
$_.Exception.Message,
"Error",
[System.Windows.Forms.MessageBoxButtons]::OK,
[System.Windows.Forms.MessageBoxIcon]::Error
)
}

}
$conectar_OnClick=
{
#TODO: Place custom script here

if ( $textBoxServerName.Text -eq '' )
{

}

else
{
$objForm.Cursor=[System.Windows.Forms.Cursors]::WaitCursor
$dataSource = $textBoxServerName.Text

$database = "master"
$connectionString = "Server=$dataSource;Database=$database;Integrated Security=True;"

$query = "select name from sys.databases where database_id > 4"

$connection = New-Object System.Data.SqlClient.SqlConnection
$connection.ConnectionString = $connectionString
$connection.Open()
$command = $connection.CreateCommand()
$command.CommandText = $query
$result = $command.ExecuteReader()
$table = new-object “System.Data.DataTable”
$table.Load($result)
#$table
$objListbox.datasource=$table.Item(0)
$connection.Close()
$objForm.Cursor=[System.Windows.Forms.Cursors]::Default

}

}

$conectar = New-Object System.Windows.Forms.Button
$conectar.Location = New-Object System.Drawing.Size(200,30)
$conectar.Size = New-Object System.Drawing.Size(60,20)
#$button2.TabIndex = 1
$conectar.Text = "Conectar"
$conectar.UseVisualStyleBackColor = $True
$conectar.add_Click($conectar_OnClick)
$objForm.Controls.Add($conectar)
$destino = New-Object System.Windows.Forms.Button
$destino.Location = New-Object System.Drawing.Size(200,80)
$destino.Size = New-Object System.Drawing.Size(50,20)
#$button2.TabIndex = 1
$destino.Text = "..."
$destino.UseVisualStyleBackColor = $True
$destino.add_Click($destino_OnClick)
$objForm.Controls.Add($destino)

$objListbox.SelectionMode = "MultiExtended"

$objListbox.Height = 70
$objForm.Controls.Add($objListbox)
$objForm.Topmost = $True

$objForm.Add_Shown({$objForm.Activate()})
[void] $objForm.ShowDialog()

$x

Gerar DACPAC usando PowerShell


O ciclo de vida da maioria dos bancos envolve desenvolvedores e DBA´s compartilhando scripts para atualização de objetos e atividades de manutenção.

Existem algumas formas de fazer isso:

  • gerar scripts de objetos
  • Proporcionar acesso as partes interessadas (piada… eu sei…) para gerar os scripts
  • DACPAC´s
  • Backup/Restore
  • etc..

Um pouco de contexto antes,,,

Um DAC (aplicativo da camada de dados) é uma entidade lógica de gerenciamento de banco de dados que define todos os objetos do SQL Server, como tabelas, exibições e objetos de instância, incluindo logons, associados a um banco de dados de usuário.

Todos tem seus prós e contras… não é a intenção deste post tratar isso…

Para quem gosta de gerar scripts dos objetos lembra que tem um outro post que faz isso…

A vantagem do DACPAC é que você pode usa-lo para comparação entre um projeto do TFS e esse pacote extraído da produção ou, caso você seja o DBA, você pode comparar esse pacote contra a sua base de produção ou uma base em homologação. Gerar os scripts para igualar os ambientes ou apenas conseguir ter ideia das diferenças.


Add-Type -AssemblyName System.Drawing
Add-Type -AssemblyName System.Windows.Forms

$data = get-date -format "_yyyyMMdd"
$WindowTitle = "Gerador de DACPAC"
$LabelPath = "Caminho:"
$LabelServer = "Servidor:"

# Create the Label.
$label = New-Object System.Windows.Forms.Label
$label.Location = New-Object System.Drawing.Size(10,20)
$label.Size = New-Object System.Drawing.Size(280,20)
$label.AutoSize = $true
$label.Text = $LabelPath

# Create the TextBox used to capture the user's text.
$textBox = New-Object System.Windows.Forms.TextBox
$textBox.Location = New-Object System.Drawing.Size(10,40)
$textBox.Size = New-Object System.Drawing.Size(575,200)
$textBox.AcceptsReturn = $true
$textBox.AcceptsTab = $false
$textBox.Multiline = $true
$textBox.ScrollBars = 'Both'
$textBox.Text = $DefaultText

# Create textbox used to file destination
$textBox2 = New-Object System.Windows.Forms.TextBox
$textBox2.DataBindings.DefaultDataSourceUpdateMode = 0
$textBox2.Location = New-Object System.Drawing.Size(65,20)
$textBox2.Name = "textBox2"
$textBox2.Size = New-Object System.Drawing.Size(150,10)
$textBox2.TabIndex = 0
# Create the Label.
$label2 = New-Object System.Windows.Forms.Label
$label2.Location = New-Object System.Drawing.Size(420,20)
$label2.AutoSize = $true
$label2.Text = $LabelServer

# Create textbox for the Server name
$textBoxServerName = New-Object System.Windows.Forms.TextBox
$textBoxServerName.DataBindings.DefaultDataSourceUpdateMode = 0
$textBoxServerName.Location = New-Object System.Drawing.Size(480,20)
$textBoxServerName.Name = "textBox2"
$textBoxServerName.Size = New-Object System.Drawing.Size(100,10)
$textBoxServerName.TabIndex = 0

# Create the OK button.
$okButton = New-Object System.Windows.Forms.Button
$okButton.Location = New-Object System.Drawing.Size(415,250)
$okButton.Size = New-Object System.Drawing.Size(75,25)
$okButton.Text = "OK"
$okButton.Add_Click({ $form.Tag = $textBox.Text; $form.Close() })

# Create the Cancel button.
$cancelButton = New-Object System.Windows.Forms.Button
$cancelButton.Location = New-Object System.Drawing.Size(510,250)
$cancelButton.Size = New-Object System.Drawing.Size(75,25)
$cancelButton.Text = "Cancel"
$cancelButton.Add_Click({ $form.Close() })

$app = New-Object -ComObject Shell.Application
$button2_OnClick=
{
try {

$browseForFolderOptions = 0
if ($NoNewFolderButton) { $browseForFolderOptions += 512 }
$folder = $app.BrowseForFolder(0, $Message, $browseForFolderOptions, $InitialDirectory)
$textbox2.Text = $folder.Self.Path #$selectedDirectory #.FileName.ToString()
}
catch {
[System.Windows.Forms.MessageBox]::Show(
$_.Exception.Message,
"Error",
[System.Windows.Forms.MessageBoxButtons]::OK,
[System.Windows.Forms.MessageBoxIcon]::Error
)
}

}
$button2 = New-Object System.Windows.Forms.Button
$button2.Location = New-Object System.Drawing.Size(215,20)
$button2.Size = New-Object System.Drawing.Size(50,20)
$button2.Text = "..."
$button2.UseVisualStyleBackColor = $True
$button2.add_Click($button2_OnClick)
# Create the form.
$form = New-Object System.Windows.Forms.Form
$form.Text = $WindowTitle
$form.Size = New-Object System.Drawing.Size(610,320)
$form.FormBorderStyle = 'FixedSingle'
$form.StartPosition = "CenterScreen"
$form.AutoSizeMode = 'GrowAndShrink'
$form.Topmost = $True
$form.AcceptButton = $okButton
$form.CancelButton = $cancelButton
$form.ShowInTaskbar = $true

# Add all of the controls to the form.
$form.Controls.Add($label)
$form.Controls.Add($label2)
$form.Controls.Add($textBox)
$form.Controls.Add($okButton)
$form.Controls.Add($cancelButton)
$form.Controls.Add($textBox2)
$form.Controls.Add($textBoxServerName)
$form.Controls.Add($button2)

# Initialize and show the form.
$form.Add_Shown({$form.Activate()})
$form.ShowDialog() > $null

$srv = $textBoxServerName.Text
$local = $textbox2.Text
$bases = $textBox.Text.Split("`n")|%{$_.trim()}
$i=0
foreach ($s in $bases)
{
Write-Progress -Activity "Gerando DACPAC" -status "Gerando DACPAC para $s" -percentComplete ($i++ / $bases.count*100)
$GoBigSon= (C:\"Program Files (x86)"\"Microsoft SQL Server"\110\DAC\bin\SqlPackage.exe /a:Extract /ssn:$srv /sdn:$s /tf:$local\$s$data.dacpac )
}

Como parece de praxe,,, tem uns pequenos bugs:

  • Quando clicar no “…” para mapear o caminho, se não aparecer uma janela pop-up, movimenta a janela do app um pouco para o lado, ela ficou atrás da janela inicial,,, isso é um bug que acontece de vez em quando,,,
  • se vc clicar diversas vezes no “…” ele vai ficar abrindo diversas vezes…
  • ainda não estou tratando outros tipos de entrada no campo texto,,,, você deve colocar um banco abaixo do outro,,,
  • e sim,,, ele fecha a janela quando você clica em OK,,,

Ele ficou até que bem simpático…

gerador dacpac p1

 

Com os campos preenchidos

gerador dacpac p2

 

E tem até barra de status… olha que chique…

gerador dacpac p3

 

Powershell, Excel e SQL uma combinação excelente


Uma coisa muito legal em automatizar tarefas é a dificuldade que temos em planejar o que vai ser executado, a forma que vai ser executado e manter da forma mais genérica possível para garantir que vai funcionar na maior parte dos ambientes sem precisar sofrer muita alteração…

Agora imagina uma rotina qualquer em que você precisa ficar pegando resultados colocando no excel para fazer alguma graça interessante para alguém…

O posh abaixo faz exatamente isso. Ele conecta em um servidor que você definir, pega o nome dos bancos e executa a mesma query em cada um dos bancos,,,

até aí nenhuma novidade, certo?

O legal é que o resultado já vai para o excel, formatado e com auto filtro… e cada guia é a resposta de um banco deste servidor.

Você pode alterar a query para executar o que quiser, fragmentação de índice? blz…. um único select de uma única base? sem problemas,,, ou verificar estimativa de compactação de tabelas e índices usando compactação PAGE? é para isso que estamos aqui….


$servers = "SERVIDOR"

#lista as bases de dados para entrar no looping
$databases =
@'
SELECT
name
FROM sys.sysdatabases
where dbid>4
'@

$resultsDB = (invoke-sqlcmd -ServerInstance $servers -Query $databases).name

#query para ser executada em cada base do looping
$query2 =
@'
declare @scanupd TABLE
(
table_name sysname NULL
,index_name sysname NULL
,partition int NULL
, index_id int NULL
,index_type nvarchar(12) NULL
,percent_scan bigint NULL
,percent_update bigint NULL
)

DECLARE @CompressionSavingsEstimate table
(
SchemaName sysname NOT NULL,
ObjectName sysname NOT NULL,
IndexName sysname NOT NULL,
IndexType nvarchar(60) NOT NULL,
PartitionNum int NOT NULL,
CompressionType nvarchar(10) NOT NULL,
[size_with_current_compression_setting (KB)] bigint NOT NULL,
[size_with_requested_compression_setting (KB)] bigint NOT NULL,
[sample_size_with_current_compression_setting (KB)] bigint NOT NULL,
[sample_size_with_requested_compression_setting (KB)] bigint NOT NULL,
percent_scan bigint NULL,
percent_update bigint NULL
PRIMARY KEY (SchemaName, ObjectName, IndexName, IndexType, PartitionNum, CompressionType)
);
DECLARE @ProcResult table
(
[object_name] sysname NOT NULL,
[schema_name] sysname NOT NULL,
[index_id] int NOT NULL,
[partition_number] int NOT NULL,
[size_with_current_compression_setting (KB)] bigint NOT NULL,
[size_with_requested_compression_setting (KB)] bigint NOT NULL,
[sample_size_with_current_compression_setting (KB)] bigint NOT NULL,
[sample_size_with_requested_compression_setting (KB)] bigint NOT NULL
);
DECLARE @SchemaName sysname;
DECLARE @ObjectName sysname;
DECLARE @IndexID int;
DECLARE @IndexName sysname;
DECLARE @IndexType nvarchar(60);
DECLARE @PartitionNum int;
DECLARE @CompTypeNum tinyint;
DECLARE @CompressionType nvarchar(60);

SET NOCOUNT ON;

DECLARE CompressedIndex INSENSITIVE CURSOR FOR
SELECT s.name AS SchemaName,
o.name AS ObjectName,
i.index_id AS IndexID,
COALESCE(i.name, '<HEAP>') AS IndexName,
i.type_desc AS IndexType,
p.partition_number AS PartitionNum
FROM sys.schemas AS s
INNER JOIN sys.objects AS o
ON s.schema_id = o.schema_id
INNER JOIN sys.indexes AS i
ON o.object_id = i.object_id
INNER JOIN sys.partitions AS p
ON o.object_id = p.object_id
AND
i.index_id = p.index_id
WHERE o.type_desc IN ('USER_TABLE','VIEW')
AND p.data_compression_desc NOT IN ('PAGE','ROW');

OPEN CompressedIndex;

WHILE 1 = 1
BEGIN
FETCH NEXT FROM CompressedIndex
INTO @SchemaName, @ObjectName, @IndexID, @IndexName, @IndexType, @PartitionNum;

IF @@FETCH_STATUS <> 0
BREAK;

SELECT @CompTypeNum = 2;
WHILE @CompTypeNum <= 2
BEGIN
SELECT @CompressionType = CASE @CompTypeNum
WHEN 0 THEN 'NONE'
WHEN 1 THEN 'ROW'
WHEN 2 THEN 'PAGE'
END;

DELETE FROM @ProcResult;

-- RAISERROR('Estimating compression savings using "%s" compression for object "%s.%s", index "%s", partition %d...', 10, 1, @CompressionType, @SchemaName, @ObjectName, @IndexName, @PartitionNum);

INSERT INTO @ProcResult
EXEC sp_estimate_data_compression_savings @schema_name = @SchemaName,
@object_name = @ObjectName,
@index_id = @IndexID,
@partition_number = @PartitionNum,
@data_compression = @CompressionType;

INSERT INTO @CompressionSavingsEstimate
(
SchemaName,
ObjectName,
IndexName,
IndexType,
PartitionNum,
CompressionType,
[size_with_current_compression_setting (KB)],
[size_with_requested_compression_setting (KB)],
[sample_size_with_current_compression_setting (KB)],
[sample_size_with_requested_compression_setting (KB)]
)
SELECT [schema_name],
[object_name],
@IndexName,
@IndexType,
[partition_number],
@CompressionType,
[size_with_current_compression_setting (KB)],
[size_with_requested_compression_setting (KB)],
[sample_size_with_current_compression_setting (KB)],
[sample_size_with_requested_compression_setting (KB)]
FROM @ProcResult;

SELECT @CompTypeNum += 1;
END;
END;

CLOSE CompressedIndex;
DEALLOCATE CompressedIndex;
insert into @scanupd(table_name, index_name, partition,index_id,index_type,percent_scan,percent_update)
SELECT o.NAME AS [Table_Name]
,x.NAME AS [Index_Name]
,i.partition_number AS [Partition]
,i.index_id AS [Index_ID]
,x.type_desc AS [Index_Type]
,i.range_scan_count * 100.0 / (i.range_scan_count + i.leaf_insert_count + i.leaf_delete_count + i.leaf_update_count + i.leaf_page_merge_count + i.singleton_lookup_count) AS [Percent_Scan]
,i.leaf_update_count * 100.0 / (i.range_scan_count + i.leaf_insert_count + i.leaf_delete_count + i.leaf_update_count + i.leaf_page_merge_count + i.singleton_lookup_count) AS [Percent_Update]
FROM sys.dm_db_index_operational_stats(db_id(), NULL, NULL, NULL) i
JOIN sys.objects o ON o.object_id = i.object_id
JOIN sys.indexes x ON x.object_id = i.object_id
AND x.index_id = i.index_id
WHERE (i.range_scan_count + i.leaf_insert_count + i.leaf_delete_count + leaf_update_count + i.leaf_page_merge_count + i.singleton_lookup_count) != 0
AND objectproperty(i.object_id, 'IsUserTable') = 1
ORDER BY [Percent_Scan] DESC
SELECT CompressionType,
IndexName,
IndexType,
ObjectName,
PartitionNum,
CASE WHEN t.[percent scan] >100 then 100 else [percent scan] END AS [percent scan],
CASE WHEN t.[percent update] >100 then 100 else [percent update] END AS [percent update],
SchemaName,
[size_with_current_compression_setting (KB)],
[size_with_requested_compression_setting (KB)],
([size_with_requested_compression_setting (KB)]/NULLIF([size_with_current_compression_setting (KB)],0)*100) AS [X percent compact],
CASE WHEN (([size_with_requested_compression_setting (KB)]/NULLIF([size_with_current_compression_setting (KB)],0)*100) <=80 OR ([size_with_requested_compression_setting (KB)]/NULLIF([size_with_current_compression_setting (KB)],0)*100) IS NULL) AND [t].[percent scan] >=t.[percent update] THEN 'Compactar' ELSE 'Provavelmente Nao' END as [Z result]
FROM (
SELECT CompressionType,
IndexName,
IndexType,
ObjectName,
PartitionNum,
sum(s.percent_scan) AS [percent scan],
sum(s.percent_update) AS [percent update],
SchemaName,
cast(AVG([size_with_current_compression_setting (KB)]) AS money) AS [size_with_current_compression_setting (KB)],
cast(AVG([size_with_requested_compression_setting (KB)]) AS money) AS [size_with_requested_compression_setting (KB)]
FROM @CompressionSavingsEstimate CSE
INNER JOIN @scanupd AS s
ON CSE.ObjectName = S.table_name
GROUP BY GROUPING SETS (
(CompressionType),
(SchemaName, ObjectName, IndexName, IndexType, PartitionNum, CompressionType)
)
)AS t
ORDER BY SchemaName, ObjectName, IndexName, IndexType, PartitionNum, CompressionType DESC;

SET NOCOUNT OFF;
'@

# abre o excel
$xl = new-object -comobject excel.application
$wb = $xl.Workbooks.Add()
#$ExcelWorkSheet = $wb.Worksheets.Add()
$xl.Visible = $true
#$rowCount = 2

foreach ($s in $resultsDB)
{
$s #pode ser removido
$ExcelWorkSheet = $wb.Worksheets.Add()
$ExcelWorkSheet.Name = $s #usa o nome do banco como nome da planilha
$rowCount = 2
$results = invoke-sqlcmd -ServerInstance $servers -Query $query2 -Database $s -querytimeout ([int]::MaxValue) #timeout nunca !!!

foreach ($r in $results)
{
$r #pode ser removido
$ColCount = 1
$ExcelWorkSheet.Cells.Item($rowCount,$ColCount).Value2 = $servers
$ColCount = 2
for ($i=1; $i -le $r.ItemArray.Count; $i ++)
{
$ExcelWorkSheet.Cells.Item($rowCount,$ColCount).Value2 = $r.ItemArray[$i-1]
$ColCount ++
}
$rowCount ++
}
# adiciona o cabeçalho das colunas
$headers = $results | Get-Member -Membertype property
$ExcelWorkSheet.Cells.Item(1,1).Value2 = 'server'
$h = 2

foreach ($header in $headers)
{
$ExcelWorkSheet.Cells.Item(1,$h).Value2 = $header.name
$h ++
}
## Formata a planilha do Excel
$listObject = $ExcelWorkSheet.ListObjects.Add([Microsoft.Office.Interop.Excel.XlListObjectSourceType]::xlSrcRange, $ExcelWorkSheet.UsedRange, $null,[Microsoft.Office.Interop.Excel.XlYesNoGuess]::xlYes,$null)
$listObject.Name = "User Table"
$listObject.TableStyle = "TableStyleLight10"
## ajusta o tamanho das colunas
$ExcelWorkSheet.UsedRange.Columns.Autofit() | Out-Null

}

PS.: ainda estou tentando entender qual a birra do powershell em querer colocar o resultado das colunas em ordem alfabética,,, assim que resolver este detalhe atualizo o código,,,

Testar Porta


Vocês sabem que se quiser testar uma porta TCP um dos métodos mais simples é basicamente um telnet Nome/IP porta.
Se o prompt sumir e o cursor ficar piscando a porta está respondendo (claro,, tirando todas as implicações de liberação de firewall e blá blá blá)
Eu precisava ficar fazendo um teste mais dinâmico, já que o telnet estabelece a conexão e espera uma intervenção para continuar eu queria apenas saber se a porta esta aberta ou não, estávamos tentando identificar uma falha se era no serviço ou na rede.open door
o script abaixo fica estabelecendo uma comunicação em um intervalo definido usando o socket TCP/IP estão podemos testar TCP e UDP bem no nível da camada e não da aplicação.
ele é bem simples, em qualquer momento que a porta não responda ele coloca a cor de fundo como vermelho.

function TestPort
{
    Param(
        [parameter(ParameterSetName='ComputerName', Position=0)]
        [string]
        $ComputerName,

        [parameter(ParameterSetName='IP', Position=0)]
        [System.Net.IPAddress]
        $IPAddress,

        [parameter(Mandatory=$true , Position=1)]
        [int]
        $Port,

        [parameter(Mandatory=$true, Position=2)]
        [ValidateSet("TCP", "UDP")]
        [string]
        $Protocol
        )

    $RemoteServer = If ([string]::IsNullOrEmpty($ComputerName)) {$IPAddress} Else {$ComputerName};

    If ($Protocol -eq 'TCP')
    {
        $test = New-Object System.Net.Sockets.TcpClient;
        Try
        {
            Write-Host "Connecting to "$RemoteServer":"$Port" (TCP)..";
            $test.Connect($RemoteServer, $Port);
            Write-Host "Connection successful" -BackgroundColor Green;
        }
        Catch
        {
            Write-Host "Connection failed" -BackgroundColor Red;
        }
        Final
        {
            $test.Dispose();
        }
    }

    If ($Protocol -eq 'UDP')
    {
        $test = New-Object System.Net.Sockets.UdpClient;
        Try
        {
            Write-Host "Connecting to "$RemoteServer":"$Port" (UDP)..";
            $test.Connect($RemoteServer, $Port);
            Write-Host "Connection successful" -BackgroundColor Green;
        }
        Catch
        {
            Write-Host "Connection failed" -BackgroundColor Red;
        }
        Final
        {
            $test.Dispose();
        }
    }
}

A forma de testar ele é bem simples:

TestPort -ComputerName Nome/IP -Port 1433 -Protocol TCP 

Legal né? só que eu precisava ficar fazendo testes direto e reto e da forma acima ele não é um looping…
logo, para fazer da forma mais simples que conheço ficou assim:

$servidor = "Nome/IP"
while (1) {
    get-date #só pra saber quando executou
    TestPort -ComputerName $servidor -Port 1433 -Protocol TCP 
    sleep -seconds 1 #tempo de espera entre as execuções
}