How can I get the Log directory? - powershell

I have recently installed SSAS on my servers and instead of going through 24 of them, I am writing a script to get back the logDir of the servers.
I am trying to do something like this:
Import-Module SqlServer
$Analysis_Server = New-Object Microsoft.AnalysisServices.Server
$Analysis_Server.connect("$server")
$Analysis_Server.ServerProperties.LogDir
I am trying to find the logDir property from here to no avail.
If I use ServerProperties, I get a list of all properties available like this:
Name : LogDir
Type :
Value : S:....
DefaultValue : \data
PendingValue : S:...
RequiresRestart : True
IsReadOnly : False
DisplayFlag : True
Category : Basic
Units : Folder
PropertyName : LogDir
FolderName
But if I do: $Analysis_Server.ServerProperties.LogDir.value or $Analysis_Server.ServerProperties.LogDir, it returns nothing.
Update
This is how I plan to run through multiple servers:
$h = #{}
Import-Csv '$csvFile' | ForEach-Object {
$h += #{$($_.Server -split '\s*,\s*') }
}
Import-Module SqlServer
foreach($server in $h.Keys){
$result = "$server"
Write-Host $result
$Analysis_Server = New-Object Microsoft.AnalysisServices.Server
$Analysis_Server.connect("$server")
$Analysis_Server.ServerProperties['LogDir'].value
}
This is my CSV file (I plan to use this for multiple purposes, so I only want to get the servers not databases for this case):
I got back this error:
Missing '=' operator after key in hash literal.

This might get you part of they way there. I don't have an analysis server at my disposal you may have to change this up a bit till you get it right.
$servers=get-content c:\temp\servers.txt
$hash=new-object hashtable
$Analysis_Server = New-Object Microsoft.AnalysisServices.Server
foreach($s in $servers)
{
$Analysis_Server.connect("$s")
$hash.add($s, $"$($Analysis_Server.ServerProperties['LogDir'])")
}
To see more great content on hashtables checkout this link: https://kevinmarquette.github.io/2016-11-06-powershell-hashtable-everything-you-wanted-to-know-about/

Related

Providing test cases to Pester V5 test

I'm trying to write a pester test (v5) to see if various services are running on remote computers. This is what I have, which works:
$Hashtable = #(
#{ ComputerName = "computer1"; ServiceName = "serviceA" }
#{ ComputerName = "computer1"; ServiceName = "serviceB" }
#{ ComputerName = "computer2" ; ServiceName = "serviceB" }
)
Describe "Checking services" {
It "check <ServiceName> is running on <ComputerName>" -TestCases $Hashtable {
( get-service -computername $ComputerName -name $ServiceName ).status | Should -be "Running"
}
}
My question is around providing the test data to the test (i.e. the list of computer names and services). Suppose I want to add more services to this list. At the moment, I would be modifying my pester file by adding more services to $Hashtable. It doesn't feel quite right to be doing this to me, and I'd like to get the approach correct at this early stage. My gut tells me that the list of services should be separated from the pester file. Then running the test would involve importing the list of services somehow. Does anyone know if I am going about this the wrong way?
Thanks for any help
Andrew
If the list of servers and services will change often, it would be a good idea to read it from a separate file, especially if you have the tests under version control. This way you can easily see in the history that only the test data has changed, but the test logic didn't.
A good file format for the given test data would be CSV:
ComputerName, ServiceName
computer1, serviceA
computer1, serviceB
computer2, serviceB
You can read the CSV using Import-Csv, but you have to convert each row to a hashtable, because Pester expects an array of hashtables for the -TestCases parameter. Import-Csv outputs an array of PSCustomObject though.
BeforeDiscovery {
$script:testCases = Import-Csv $PSScriptRoot\TestCases.csv | ForEach-Object {
# Convert row (PSCustomObject) to hashtable.
$hashTable = #{}
$_.PSObject.Properties | ForEach-Object { $hashTable[ $_.Name ] = $_.Value }
# Implicit output that will be captured in array $script:testCases
$hashTable
}
}
Describe "Checking services" {
It "check <ServiceName> is running on <ComputerName>" -TestCases $script:testCases {
( get-service -computername $ComputerName -name $ServiceName ).status | Should -be "Running"
}
}
Note: While not strictly necessary I have put the code that reads the test cases into the BeforeDiscovery section, as suggested by the docs. This makes our intentions clear.

Why order is not maintained while reading a text file from Powershell?

Below is my Powershell script:
$server_file = 'serverlist.txt'
$servers = #{}
Get-Content $server_file | foreach-object -process {$current = $_.split(":"); $servers.add($current[0].trim(), $current[1].trim())}
foreach($server in $servers.keys){
write-host "Deploying $service on $server..." -foregroundcolor green
}
My serverlist.txt looks like this:
DRAKE : x64
SDT: x64
IMPERIUS : x64
Vwebservice2012 : x64
Every time I run this script, I get IMPERIUS as my server name. I would like loop through the servers in the order they are written in serverlist.txt.
Am I missing anything in Get-Content call?
Don't store servers in a temporary variable.
The iteration order of hashtables (#{}) is not guaranteed by the .NET framework. Avoid using them if you want to maintain input order.
Simply do:
$server_file = 'serverlist.txt'
Get-Content $server_file | ForEach-Object {
$current = $_.split(":")
$server = $current[0].trim()
$architecture = $current[1].trim()
Write-Host "Deploying $service on $server..." -ForegroundColor Green
}
Note: Even if it probably won't make much of a difference in this particular case, in general you always should explicitly define the file encoding when you use Get-Content to avoid garbled data. Get-Content does not have sophisticated auto-detection for file encodings, and the default it uses can always be wrong for your input file.

Test-Path PowerShell Issue

I am trying to search several servers to see if a specific Registry key exists.
It looks like the code below is working, but as I start to add the final part of the key, it stops "finding" stuff. I can start to add a*, then ab* as the last key, but as soon as I get to the third character or even the full string that I know is there, it comes back False saying it did not find it.
$servers = Get-Content c:\input.txt | `
Select-Object #{l='ComputerName';e={$_}},#{l='KeyExist';e={Test-Path "HKLM:\System\CurrentControlSet\services\*abcdefg*" }}
$servers | Format-Table -AutoSize
Your problem is that you run Test-Path against the local computer for each remote server name. Unfortunately Test-Path doesn't support querying remote registries.
You could use WMI:
$RegProv = [wmiclass]"\\$servername\root\default:StdRegProv"
if($RegProv.EnumKey(2147483650,"System\CurrentControlSet\services").sNames -like 'abc*'){
# key starting with abc exists
}
Wrap it in your calculated property like this:
#{Name='KeyExists';Expression={[bool](([wmiclass]"\\$_\root\default:StdRegProv").EnumKey(2147483650,"System\CurrentControlSet\services").sNames -like 'abc*')}}
You can check the remote registry like this :
So for each server it will get the registry value and it will store the value in the arraylist and will display the final result.
Presently in your code, you are basically checking locally only.
#####Get Registry Value ####
$main = "LocalMachine"
$path= "registry key path"
$servers = Get-Content c:\input.txt #-- Get all the servers
$arr=New-Object System.Collections.ArrayList
foreach ($Server in $servers)
{
$reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($main, $Server)
$regKey= $reg.OpenSubKey($path)
$Value = $regkey.GetValue($key)
$arr.Add($Value)
}
$arr
Note: Change the placeholders accordingly

PowerShell Runspaces, outputs and variables

I'm trying to understand Runspaces in PowerShell. I know about the PoshRSJob-Module, but I'd like to create my Runspace Jobs by myself.
This is my code, mostly taken out from this blog:
$Computer = "somename"
[runspacefactory]::CreateRunspacePool() > $null
$SessionState = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault()
$RunspacePool = [runspacefactory]::CreateRunspacePool(1,5)
$RunspacePool.Open()
1..2 | % {
$PowerShell = [powershell]::Create()
$PowerShell.RunspacePool = $RunspacePool
$PowerShell.AddScript({
param(
$Computer
)
$Computer
}) > $null
$PowerShell.AddParameter($Computer)
$Invoke = $PowerShell.BeginInvoke()
while (!($Invoke.IsCompleted)) {sleep -Milliseconds 2}
$Data = $PowerShell.EndInvoke($Invoke)
Write-Host $Data -f Red
}
Three question are in my mind:
Will I be able to return a Value in a Variable and use it for further use inside the script after finishing the Job?
Why is my $Data Variable empty?
In the Script I have so far, what is creating the following output? If I $null the invocation like this $Invoke = $PowerShell.BeginInvoke() > $null
, the script doesn't work properly anymore and still creates this output
Commands : System.Management.Automation.PSCommand
Streams : System.Management.Automation.PSDataStreams
InstanceId : 3b91cfda-028e-4cec-9b6d-55bded5d9d3c
InvocationStateInfo : System.Management.Automation.PSInvocationStateInfo
IsNested : False
HadErrors : False
Runspace :
RunspacePool : System.Management.Automation.Runspaces.RunspacePool
IsRunspaceOwner : False
HistoryString :
I don't understand your first question.
For the second question, I think it's because you're using $PowerShell.AddParameter($Computer).
Try $PowerShell.AddArgument($Computer) instead. AddArgument is for adding a value that gets implicitly (positionally) bound to a parameter. AddParameter is for adding a named parameter. The overload of AddParameter that takes just a string is for [Switch] parameters.
For your third question, I think it's $RunspacePool.Open() that's giving you that output.
When trying to determine these things, look for lines, especially with method calls, that have no left-hand assignment; so things you aren't assigning to a variable, as that's generally how these values get put into the output stream.

Powershell [Ref] Value not updating main object

I'm running Powershell V2 on XP:
This is part of a larger script and I'm noticing some anomolies with the way I'm able to use reference objects to update the "master" object. This started off as a way to avoid typing out the complicated name of the property each time - I could easily expand out to the full name but now the reason for this behaviour is seriously bugging me.
[CmdletBinding()]
Param()
Function Breakhere {Write-Verbose " "}
Set-PSBreakpoint -Command Breakhere
$Data = #"
UserID
MyUser1
MyUser2
User3
"#
$Data = $Data|ConvertFrom-Csv
$Domains = "DomainA","DomainB"
$Props = "ParentContainer","AccountIsDisabled","employeeNumber"
$Connection = New-Object HashTable
ForEach ($Domain in $Domains)
{
Write-Verbose "Current Domain: $Domain"
# Add necessary headers to main data
$text1 = "$($Domain)_ADObject"
$text2 = "$($Domain)_Confidence"
$Data = $Data |Select *,$text1
$Data = $Data |Select *,$text2
#Bind to each domain and save the connection contexts into a hashtable
Write-Verbose "Binding to $Domain"
$Connection.Add($Domain,(Connect-QADService -service $Domain))
}
ForEach ($User in $Data)
{
ForEach ($Domain in $Domains)
{
$User."$($Domain)_ADObject" = Get-QADUser -Connection $Connection[$Domain] -SamAccountName $User.UserID -DontUseDefaultIncludedProperties -IncludedProperties $Props|Select $Props
# Referencing the confidence parameter does not seem to work.
$CF = [ref]$User."$($Domain)_Confidence"
# Weirdly, this one does work.
$AD = [ref]$User."$($Domain)_ADObject"
If ($AD.Value)
{
$CF.Value = 1
Breakhere # Break here and allow for opportunity to inspect $user and $CF objects
If ($AD.Value.AccountIsDisabled)
{
Write-Verbose "$Domain\$($User.UserID): Account Disabled"
$CF.Value *= 0.8
}
}
Else
{
Write-Verbose "$Domain\$($User.UserID): No AD Object found"
$CF.Value = 0
}
}
} #End ForEach $UserID
At the breakpoint, if I query $User, I receive something similar to the following:
UserID : MyUser1
DomainA_ADObject : #{ParentContainer=DomainA/Users; AccountIsDisabled=False; employeeNumber=123456}
DomainA_Confidence :
DomainB_ADObject :
DomainB_Confidence :
All good. Should I wish, I can even use the $AD ref object and update DomainA_ADobject:
$AD.Value.employeeNumber = 9999
$User
UserID : MyUser1
DomainA_ADObject : #{ParentContainer=DomainA/Users; AccountIsDisabled=False; employeeNumber=9999}
DomainA_Confidence :
DomainB_ADObject :
DomainB_Confidence :
However, try this with the $CF ref and the same thing doesn't happen
$CF.Value = 2
$CF
Value
-----
2
$User
UserID : MyUser1
DomainA_ADObject : #{ParentContainer=DomainA/Users; AccountIsDisabled=False; employeeNumber=9999}
DomainA_Confidence : *<====== Expecting this to update!*
DomainB_ADObject :
DomainB_Confidence :
Why the difference? Is there any way to query a [ref] object and see what it's pointing to? I can't see why one of these is working and the other isn't. They both seem to be set up in the same way. Tried this in ISE and console, same behaviour in both.
My guess is, that this is caused by dots in the name of domains.
Reducing this to basics $CF.Value = 1 does someting like
$Data[0].domain.name.net.au_Confidence.value = 1
This will not work. This will:
$Data[0]."domain.name.net.au_Confidence".value = 1
Perhaps this will fix it?:
$CF = [ref]$User."`"$($Domain)_Confidence`""