Test-Path PowerShell Issue - powershell

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

Related

If else statement inside foreach results in overwrite each time it loops

I apologize for the unclear title. I'm finding it hard to articulate my problem. I'm writing a script in powershell for the first time. I primarily use python for short scripts but for this task I'm forced to use powershell because of some limitations where I need to use powercli cmdlets. Let me quickly explain the problem. This is to create and/or assign tags to vms in vsphere.
I read the list of VMs into a variable $vms2tag. These are the ones that need to be tagged.
I read a json file into a variable and create tag name and description variables based on the data in the json (there's key value pairs that i can directly plug into the names and descriptions) This file also has a 'Server' key which has a value of the VM name exactly as it would appear in "Output-VM.csv" file. This file has data about every single VM that exists. Only ones that need tagged the ones in $vms2tag
Based on some if else conditions like if tag category exists, or if tag exists, it will either create one or use/assign one.
Basically the following code "works" in the sense it will create these tags BUT, it will quickly get overwritten by the next $vm until it keeps overwriting each time and the only tag that sticks around on all the $vms is the one created for the last VM in the list.
$myJson = Get-Content 'C:\For-Powershell.json'| Out-String | ConvertFrom-Json
$vms2tag = Get-Content 'C:\Output-VM.txt'
foreach ($vm in $vms2tag) {
For ($j=0; $j -lt $myJson.Length; $j++) {
if ($vm -eq $myJson.Server[$j]) {
Write-Output "Match!"
# Variables for Application Owner
$nameAO = [string]$myJson.Application_Owner[$j]
$descriptionAO = [string]$myJson.Application_Owner[$j]
# check if Tag Category and/or Tag exist
if ((Get-TagCategory -Name "app_owner") -eq $null) {
New-TagCategory -Name "app_owner" -Cardinality "Multiple"
}
if ((Get-Tag -Category "app_owner" | Set-Tag -Name $nameAO -Description $descriptionAO) -eq $null) {
$myTagAO = Get-TagCategory -Name "app_owner" | New-Tag -Name $nameAO -Description $descriptionAO
New-TagAssignment -Tag $myTagAO -Entity $myJson.Server[$j]
}
else {
$myTagAO = Get-Tag -Category "app_owner" | Set-Tag -Name $nameAO -Description $descriptionAO
New-TagAssignment -Tag $myTagAO -Entity $myJson.Server[$j]
}
}
}
}
I tested while the script runs and the tag is properly applied to the VM based on its data but when I refresh it after the script completes, all the tags on each VM exist but they are incorrect as they contain the information that's valid only for the last VM in the $vms2tag list. It seems pretty simple but I just don't see where I'm messing up. My guess is something with if else statements is nested incorrectly? It took me a while (~6 hours) to get this to work as I had other issues with the script but when I finally got the tags to correctly set based on the other conditions, I ended up with this problem so it's possible I'm just burnt out and not seeing it.
The problem is with the Tag logic. The following line is overwriting existing tags every loop:
if ((Get-Tag -Category "app_owner" | Set-Tag -Name $nameAO -Description $descriptionAO) -eq $null) {
The Set-Tag cmdlet should never be used in a test to find existing tags.
I would write the test and assignment block like the following:
$myTagAO = Get-Tag -Category "app_owner" -Name $nameAO -ErrorAction SilentlyContinue
if ($myTagAO -eq $null) {
$myTagAO = Get-TagCategory -Name "app_owner" | New-Tag -Name $nameAO -Description $descriptionAO
}
New-TagAssignment -Tag $myTagAO -Entity $myJson.Server[$j]
This ensures that each tag is only created once, with the appropriate description.

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.

Better way to query remote server registries?

I've built this small block of code to query and store the values of a group of servers, which seems to work fine, however I'd like to know if there is a "pure PowerShell" way to do this.
$eServers = Get-ExchangeServer
$Servers = $eServers | ?{$_.Name -like "Delimit_server_group"}
foreach ($server in $Servers)
{
[string]$Key1 = "\\$server\HKLM\SYSTEM\CurrentControlSet\Control\"
[string]$rKeys += (REG QUERY "$key1" /s)
}
You can use the RegistryKey class to open a remote registry:
$RemoteHKLM = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine',$server)
$RemoteKey = $RemoteHKLM.OpenSubKey('SYSTEM\CurrentControlSet\Control')
# Following will return all subkey names
$RemoteKey.GetSubKeyNames()
You'll have to implement recursive traversal yourself if you need functionality equivalent to reg query /s
Matthias' answer is probably your best option, but there are other approaches you could take as well. If you have PSRemoting enabled on your systems, you could for instance invoke remote commands like this:
$key = 'HKLM:\SYSTEM\CurrentControlSet\Control'
Invoke-Command -Computer $Servers -ScriptBlock {
Get-ChildItem $args[0] | Select-Object -Expand Name
} -ArgumentList $key

perform nslookup from PowerShell

I'm writing a powershell to exact the ip from a server name, which need me to embed the nslookup code into my powershell
how can I do the intergrating work?
Can any body help me?
Add-PSSnapin Microsoft.SharePoint.PowerShell
$web = Get-SPWeb -Identity “http://nycs00058260/sites/usitp“
$server_status = "PROD"
$list=$web.Lists[”DNS_Status”]
$items = $list.items
Foreach($item in $items){
$item_name = $item["Server_name"] #need to get the ip by this name
/*nslook up*/
$item_name.update()
}
If you install the PSCX module, it comes with a cmdlet Resolve-Host which handles name lookups.
Absent that, this one-liner will do the job
[System.Net.Dns]::GetHostAddresses("www.msn.com")
You can also pass in an IP address - but the results will be different.
See also http://blogs.msdn.com/b/powershell/archive/2006/06/26/647318.aspx & http://powershell.com/cs/media/p/210.aspx
PowerShell 3.0 on Windows 8 and higher comes with a Resolve-DnsName cmdlet that will get this information:
(Resolve-DnsName $server_name)[0].IpAddress
Simply use :
Resolve-DnsName monServer | ? { # make selection here } | % { $_.IPAdress } | select-object -first 1
#Here is a far better method for nslookup
# HOWTO ensure an nslookup results no errors but still gives the original names and column separations
$day = Get-Date -Format yyyyMMdd #<-- HOWTO set the day variable for today
$ErrorActionPreference = ‘SilentlyContinue’ #<-- HOWTO turn off error messages
$WarningActionPreference = 'SilentlyContinue' #<-- HOWTO turn off warning messages
$servers = Get-Content .\input\servers.txt
Foreach ($server in $servers){
$result = Resolve-DnsName $server -Server $env:LOGONSERVER.Remove(0,2) -Type ALL #<-- NSLOOKUP using your logon server
write-host ($server+","+$result.Name+","+$result.IPAddress) #<-- HOWTO Write two variables separated by a comma
}
$ErrorActionPreference = ‘SilentlyContinue’ #HOWTO turn on error messages
$WarningActionPreference = 'SilentlyContinue' #HOWTO turn on warning messages