Unable to query [adsisearcher] for trusted domains (PowerShell) - arrays

I wanted to query [adsisearcher] to get me the OU info for a few servers for servers in trusted & non trusted domains.
$session = New-PSSession -ComputerName icvmm02
Invoke-Command -Session $session -ScriptBlock {
$compname= (Get-WmiObject "Win32_Computersystem" -namespace "root\CIMV2" -computername $comp).name
$searcher = [adsisearcher]"(&(ObjectCategory=computer)(Name=$compname))"
$ou= $searcher.FindOne() | Select-Object path
$serverou = $ou.path
}
$adou= (Invoke-Command -Session $session -ScriptBlock { $serverou })
Get-PSSession | Remove-PSSession
for servers in trusted domains im passing a $cred = get credentials while creating a pssession, but when i run
$compname= (Get-WmiObject "Win32_Computersystem" -namespace "root\CIMV2" -computername $comp).name
$searcher = [adsisearcher]"(&(ObjectCategory=computer)(Name=$compname))"
$ou= $searcher.FindOne() | Select-Object path
it gives me an error as
Exception calling "FindOne" with "0" argument(s): "An operations error occurred.
"
At line:1 char:27
+ $ou= $searcher.FindOne <<<< () | Select-Object path
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodException
$serverou = $ou.path
Could some one please help me in sorting this out.

I don't the cause but the ADSI interface just doesn't work in remote sessions. I can't even get the DN of the domain of a computer in my domain. I can get this to run locally but not remotely:
icm { ([adsi]"").distinguishedName } #works
icm -Session $s -ScriptBlock { ([adsi]"").distinguishedName } #doesn't work

Looks like there's a problem with the FindOne method call - you can find out more about the $searcher object with this;
$searcher | gm
the findOne method should be there with a list of the parameters it takes.
Though, I've just tried it on the type:
[adsisearcher] | gm | sort name
and there's no FindOne method - are you sure it's a method of adsisearcher?

Related

Could Not Load Assembly MICROSOFT.SQLSERVER.BATCHPARSER on 32-BIT environment

I'm posting my problem here, hoping someone may help me to figure the issue.
So, for one of my clients I've developed a PS script that retrieve a table for a database and export it as a CSV directly to a Blob Storage. My script works fine in a 64-Bit environment. However, I cannot run it in a 32-Bit environment. I need to run it in a 32-Bit environment because the scheduler used by the client is a 32-Bit tool.
On my side, I've tried every thing I've already found around the net on this subject with no luck.
My problem as I said above is that I fail to run my script on a 32-Bit environment. I'm putting a screenshot of booth environment so you can see what I'm having.
The Green square is the expected result. The Yellow one is the error I'm having.
The Blue squares shows booth SqlServer Modules I downloaded (x86 & 64).
I have the same behavior from a CMD SHELL.
So My questions are:
Is there anyway to make this script working on a 32-Bit environment?
Else Is there anyway to force a 32-BIT CMD SHELL to open a 64-Bit session on PowerShell ?
Here is the FUll PS SCript :
param (
[String]$SourceServer="" ,
[String]$SourceDatabase="" ,
[String]$DestinationStorageAccountName = "",
[String]$DestinationStorageAccountContainrerName= "",
[String]$DBUser = "",
[String]$DBUserPWD = ""
)
FUNCTION Write-ToBlobStorage{
[CmdletBinding()]
param (
[Parameter(Mandatory)][String]$ResultString,
[Parameter(Mandatory)][String]$DestinationStorageAccountName,
[Parameter(Mandatory)][String]$DestinationStorageAccountContainrerName,
[Parameter(Mandatory)][String]$FileName
)
write-host "Clear existing identies to keep cache fresh"
Clear-AzContext -force
write-host "Authenticate using the Managed identity"
$account = Connect-AzAccount -identity
if(-not $account.Context.Subscription.Id)
{
write-error "Failed to authenticate with the Managed identity. Ensure VM has a Managed identity enabled and is assigned the correct IAM roles"
return
}
write-host "Get storage context"
$context = New-AZStorageContext -StorageAccountName $DestinationStorageAccountName
write-host "Get storage Container"
$container=Get-AzStorageContainer -Name $DestinationStorageAccountContainrerName -Context $context
write-host "Writing Result to storage"
$content = [system.Text.Encoding]::UTF8.GetBytes($ResultString)
$container.CloudBlobContainer.GetBlockBlobReference("$FileName.csv").UploadFromByteArray($content,0,$content.Length)
}
#Import-Module 'Az.KeyVault' -Force
#Import-Module -Name 'C:\Program Files\WindowsPowerShell\Modules\SqlServer' -Force
Import-Module -Name 'C:\Program Files (x86)\WindowsPowerShell\Modules\SqlServer' -Force
$TLS12Protocol = [System.Net.SecurityProtocolType] 'Ssl3 , Tls12'
[System.Net.ServicePointManager]::SecurityProtocol = $TLS12Protocol
$Query = "SELECT ##SERVERNAME"
$Result = Invoke-Sqlcmd -ServerInstance $SourceServer -Database $SourceDatabase -Query $Query | ConvertTo-Csv -Delimiter '|' -NoTypeInformation
$ResultString = $Result -join "`r`n"
Write-ToBlobStorage -ResultString $ResultString -DestinationStorageAccountName $DestinationStorageAccountName -DestinationStorageAccountContainrerName $DestinationStorageAccountContainrerName -FileName "TMP_Flux"
write-host "--- ALL DONE---"
And Here is The error for the 32-Bit :
Invoke-Sqlcmd : Could not load file or assembly
'Microsoft.SqlServer.BatchParser, Version=15.100.0.0, Culture=neutral,
PublicKeyToken=89845dcd8080cc91' or one of its dependencies. The system cannot
find the file specified.
At C:\temp\ExportToBlobScript\ExportToBlob.ps1:87 char:11
+ $Result = Invoke-Sqlcmd -ServerInstance $SourceServer -Database $Sour ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Invoke-Sqlcmd], FileNotFoundEx
ception
+ FullyQualifiedErrorId : System.IO.FileNotFoundException,Microsoft.SqlServ
er.Management.PowerShell.GetScriptCommand
Write-ToBlobStorage : Cannot bind argument to parameter 'ResultString' because
it is an empty string.
At C:\temp\ExportToBlobScript\ExportToBlob.ps1:91 char:35
+ Write-ToBlobStorage -ResultString $ResultString -DestinationStorageAc ...
+ ~~~~~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [Write-ToBlobStorage], Parameter
BindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorEmptyStringNotAll
owed,Write-ToBlobStorage
--- ALL DONE---
And Here is the result for the 64-Bit:
Clear existing identies to keep cache fresh
Authenticate using the Managed identity
Get storage context
Get storage Container
Writing Result to storage
--- ALL DONE---
Many Thanks for all of you suggestions.
Not sure. But since you're only using Invoke-SqlCmd to run a query, you can eliminate the dependence on the SqlServer powershell module by using ADO.NET directly from PowerShell. The SQL Server client libraries are part of the .NET framework, so they will be available on any Windows box. So something like:
function Invoke-SqlCmd-Custom{
[CmdletBinding()]
param (
[Parameter(Mandatory)][String]$ServerInstance,
[Parameter(Mandatory)][String]$Database,
[Parameter(Mandatory)][String]$Query
)
$con = new-object System.Data.SqlClient.SqlConnection
$con.ConnectionString = "Server=$ServerInstance;Database=$Database;Integrated Security=true"
try
{
$con.Open()
$cmd = $con.CreateCommand()
$cmd.CommandText = $Query
$dt = new-object System.Data.DataTable
$rdr = $cmd.ExecuteReader()
$dt.Load($rdr)
return $dt.Rows
}
finally
{
$con.Close()
}
}

Creating a CSV file to use for starting services on multiple servers

I have created a tsv file to list Servers and Services as follows:
TSV File as follows:
Hostname Services
=========================
Server01 SP4AdminV4,SPTraceV4,SPWriterV4,WAS,W3SVC
Server02 SP4AdminV4,SPTraceV4,SPWriterV4,WAS,W3SVC,SPSearchHostController, OSearch16
PowerShell command
Import-csv C:\ServerServerList.tsv
$Services = $_.Services -Split ','
Start-Service -Computer 'Server01' -Name $Services
I then get the following error:
Start-Service: Cannot Bind Argument to parameter 'Name' because it is an empty string.
At Line:3 char:43
+ Start-Service -Computer $_.Hostname -Name **$Services**
+
+categoryinfo: InvalidData (:) [Start-Service], ParameterBindingValidationException
+FullyQualifiedErrorId: ParameterArgumentValidationErrorEmptyStringNotAllowed,
Microsoft.Powershell.Commands.StartServiceCommand
There are a couple of problems I can spot at a glance. First you don't seem to be assigning $Services correctly. 2nd you Start-Service doesn't have a -ComputerName parameter.
To get around that you can use the use Get-Service in conjunction with Set-Service implicitly using the -InputObject parameter via the pipeline.
Import-csv C:\ServerServerList.tsv -Delimiter "`t" |
ForEach-Object{
$Services = $_.Services -Split ','
Get-Service -Computer $_.HostName -Name $Services
} |
Start-Service
I'm assuming this is a Tab separated file as you described. Also assuming the services are listed in such a way to make the split proper.
The loop sends [System.ServiceProcess.ServiceController] objects down the pipeline. Those are bound to the -InputObject parameter. Internally Start-Service uses the .MachineName property to make the change on the remote system.
Warning: Get/Set-Service doesn't always report errors properly in this kind of scenario. The primary obstacle I've encountered is misleading errors and/or silent failures in cases where the operator account doesn't have access to the remote system.
I'm sure there's easier ways to go about this but, here's what I got:
# import csv and save to variable.
$CSV = Import-Csv -Path 'C:\ServerServerList.tsv'
# Use a foreach loop to iterate throw csv
# one row at a time - assigning the current iteration to $Row.
foreach ($Row in $CSV) {
# Split services into an array
$Services = ($Row.Services -split ',').Trim()
# Invoke the start-service call due to it not
# having it's own -ComputerName parameter to work with.+
Invoke-Command -ScriptBlock {
# Start the service using the newly created array of $Services.
# Note: we must use a *Remote Variable* to pass over our local variable
# by specifying the $using: keyword.
Start-Service -Name $using:Services -PassThru -WhatIf
} -ComputerName $Row.Hostname
}
- This is untested -

Passing an array to Get-Job

I am having trouble getting an array passed to a scriptblock in Start-Job. Can you tell me what I might be doing wrong?
$bounceBlock = {
param(
[string[]]$list,
[System.Management.Automation.PSCredential]$cred
)
Add-PSSnapin VMware.VimAutomation.Core | Out-Null
Set-PowerCLIConfiguration -DefaultVIServerMode Multiple -Scope User -InvalidCertificateAction Ignore -Confirm:$false | Out-Null
Connect-VIServer -Server servername -Credential $cred -AllLinked
Get-VM -Name $list
}
if ($targets) {
$activeTargets = $targets | Get-Random -Count $prodTargets.Count
$counter = [pscustomobject] #{Value = 0}
$groupSize = 50
$groups = $activeTargets | Group-Object -Property {[math]::Floor($counter.Value++ / $groupSize)}
$connection = Connect-VIServer -Server servername -Credential $cred -AllLinked
if ($connection -match "servername") {
foreach ($group in $groups) {
while ((Get-Job -State Running).Count -ge 5) {
Start-Sleep -Seconds 5
}
Start-Job -ScriptBlock $bounceBlock -ArgumentList (,$group.Group.ServerName),$cred
}
Disconnect-VIServer * -Force -Confirm:$false
}
}
I basically split an array into chunks of 50 (working) then try to run them as jobs. The error I get looks like it's trying to run Get-VM for a single server, named all 50 values appended together.
I am certainly no expert with PS, but first to address how you're passing in the appended list of servers; I do something similar with Azure VMs using Get-AzureVM and I pass in my list of VM names in a System.Array to functions or cmdlets such as a variable such as $theVMs= "MyServer1","MyServer2","MyServer3"
and then I execute a foreach loop over ($vm in $theVMs) and then perform the actions such as Get-VM sequentially. I do this sequentially since PS has some much lower limits, 5 per my experience, doing this via a parallel for loop.
A typical way I interact with the VMs remotely and create a PS Job per each is to use
$uri = Get-AzureWinRMUri -ServiceName $svc -Name $vmname
Invoke-Command -ConnectionUri $uri -Credential $creds **-JobName
$jobname**
-ArgumentList $vmname -ScriptBlock {
param([string]$thevm) ...
}
This requires the InstallWinRMCertAzureVM.ps1 script which is discussed and available at http://blogs.technet.com . I use this for between 30 servers regularly.
Just wanted everyone to know in case they run into a similar problem, this is an issue with running Get-VM, and it exists whether running it in a job or workflow. VMWare is aware of this issue.

Powershell Script to Start Service if it is Stopped and wait for minute and send an email notification

I am very new to Powershell and in learning stage, I have tried to create an script to do automate below task. This script is not working as i am expected. Could you please review it and give me some help on it.
My task is,
I am trying to find out SQL Services (more than one SQL services in multiple servers) which are in stopped state and trying to start it, Waiting for an minute to complete the service start and verifying the service status again. If still it is stopped state i am trying to sending an email to setup of people for an action.
Could you please review the below code and correct the mistake, i tried to find it but unable to do
#Define servers & Services Variables
$Servers = GC "E:\Bhanu\SQLServer.txt"
$Services = GC "E:\Bhanu\SQLService.txt"
#Function Call
Function ServiceStatus ($Servers, $Services)
{
foreach ($Server in $Servers)
{
foreach ($Service in $Services)
{
$Servicestatus = get-service -ComputerName $Server -Name $Service
if ($Servicestatus.Status -eq "Stopped")
{
Start-service $Service
Start-Sleep -Seconds 60
$ServiceStatus1 = Get-Service -ComputerName $Server -Name $Service
if ($Servicestatus1.Status -eq "Stopped")
{
FuncMail -To “abc#gmail.com” -From “abc#gmail.com” -Subject $Server + $Service "fails to Start, Take immediate Action to avoid Impact” -Body $ServiceName "Service fails to Start, Take immediate Action to avoid Impact” -smtpServer “servername”
}
}
}
}
}
function FuncMail
{
#param($strTo, $strFrom, $strSubject, $strBody, $smtpServer)
param($To, $From, $Subject, $Body, $smtpServer)
$msg = new-object Net.Mail.MailMessage
$smtp = new-object Net.Mail.SmtpClient($smtpServer)
$msg.From = $From
$msg.To.Add($To)
$msg.Subject = $Subject
$msg.IsBodyHtml = 1
$msg.Body = $Body
$smtp.Send($msg)
}
servicestatus $Servers $Services
Please let me know if you need anything here from my end
Hi this isn't the best approach and i'm doing it in quick way.
note %=foreach-object; ?=Where-Object.
You have to save your password on one file if your smtp-server require authentication otherwise don't run it using read-host -assecurestring | convertfrom-securestring | out-file "C:\Secure\Password.txt"
I'm also assuming you have your servers saved on one file.
My solution is to start all sql server service if you want to start specific just save the service name on one file on separate line.
The code to execute bellow.
#Loading Server and service details
$Services=Get-content C:\PS\Service.txt
$servidores=get-content C:\PS\Servers\Servers.txt
#Loading Mail credential
$Mailpasswordpath="C:\PS\Securestring.txt"
$Mailusername="DOmain\User"
$password=cat $Mailpasswordpath |ConvertTo-Securestring
$Cred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $Mailusername,$password
$servidores|Foreach-Object{Get-Service -ComputerName $_ -Name $Services }| #Get the services running on all servers
Where-Object{$_.Status -eq "Stopped"}| #Which status is equal stopped
Foreach-Object{
$_.Start(); #try to start
Start-Sleep -Seconds 60; #wait one minute
$_.Refresh(); #refresh then service to update status
#validate is status still stopped
if($_.Status -eq "Stopped")
{
#LOADING Mail details
$To="user#domain.com"
$subject="$($_.MachineName) $($_.Name) fails to Start, Take immediate Action to avoid Impact"
$From="ServiceStatus#domain.com"
$smtp="Server.domain.com"
$body="$($_.Name) Service fails to Start, Take immediate Action to avoid Impact"
#Sending email to notify
Send-MailMessage -To $To -Subject $subject -From $From -SmtpServer $smtp -Body $body -Credential $Cred
}
}
P.S: It's not the best approach I only decide to solve this problem. if you want we can create a function together later just test it first.

How to find the client name who has logged in to machine?

We are trying to find from which machine a user has taken rdp .
Using "quser" utility we are able to get all the information about logged in user except client name.
Following is the command
function Get-LoggedOnUser
{
param([String[]]$ComputerName = $env:COMPUTERNAME)
$ComputerName | ForEach-Object {
(quser /SERVER:$_) -replace '\s{2,}', ',' |
ConvertFrom-CSV |
Add-Member -MemberType NoteProperty -Name ComputerName -Value $_ -PassThru
}
}
It displays all the information which can be provided in Windows task manager except client Name .
How to get client Name using powershell?
I doubt if WMI has a way to do this. You can check the PSTerminal Services module and it has a Get-TSSession cmdlet which does the same job you are looking for.
http://archive.msdn.microsoft.com/PSTerminalServices
This module uses binary Cassia namespace.
You can use this:
http://gallery.technet.microsoft.com/scriptcenter/0e43993a-895a-4afe-a2b2-045a5146048a
and look for the logged on user with a logon type of RemoteInteractive
You can read that information from the Security eventlog (look for logon type 10):
$username = '...'
$eventID = 4624 # 526 on Server 2003 and earlier
$date = (Get-Date).Date
$pattern = 'logon type:\s+10[\s\S]+source network address:\s+(\S+)'
Get-EventLog Security -InstanceId $eventID -EntryType SuccessAudit `
-After $date -Message '*$username*' `
| ? { $_.Message -match $pattern } `
| % { $matches[1] } `
| select -Unique
Note that on Server 2003 and earlier you need to check for event ID 528 instead of 4624.
References:
http://technet.microsoft.com/en-us/library/cc787567
http://support.microsoft.com/kb/977519

Resources