PowerShell - Use Dynamically Created Variable Name Within a Variable Name - powershell

So I have a powershell script that I am trying to get up and running. Most of it works but what I am trying to do to make it as easy as possible to run periodically is to have it reference a list of numbers (IPs) in a text file, and then create a new variable for each line of the text file. This part does work using the following.
$iplist = get-content c:\powershell\ips.txt | where-object { $_.Trim() -ne '' }
$startnum = 0
foreach($line in $iplist){
$startnum++
new-variable -name "ip$startnum" -Value $line -Force
}
This is great, but later on in the script it has to use the number stored in each of those dynamically created variables in two other parts of the script. One part is where is reverses the IP address and then stores its reversed variant in another variable, and on another part it uses that reversed IP address as part of a lookup using [System.Net.DNS].
So basically, using that first snippet of code the script does the following, using a text file called ips.txt with 1.2.3.4 on the first line and 5.6.7.8 on the second line
$ip1 = 1.2.3.4
$ip2 = 5.6.7.8
Then I want to take those $ip1 and $ip2 values and reverse them. I know the reverse part works cause it works with a static input, but when i try it with variables it doesn't work, below is a snippet of the code i'm trying to do this with. Its basically an updated version of the code snippet from above.
$iplist = get-content c:\powershell\ips.txt | where-object { $_.Trim() -ne '' }
$startnum = 0
foreach($line in $iplist){
$startnum++
new-variable -name "ip$startnum" -Value $line -Force
new-variable -name "ipParts$startnum" -Value "$ip$startnum".Split('.')
[array]::Reverse($ipParts$startnum)
$ipparts$startnum = [string]::join('.',$ipParts$startnum)
}
When I run that though I get the following errors
At line:8 char:30
+ [array]::Reverse($ipParts$startnum)
+ ~
Missing ')' in method call.
At line:8 char:30
+ [array]::Reverse($ipParts$startnum)
+ ~~~~~~~~~
Unexpected token '$startnum' in expression or statement.
At line:4 char:26
+ foreach($line in $iplist){
+ ~
Missing closing '}' in statement block or type definition.
At line:8 char:39
+ [array]::Reverse($ipParts$startnum)
+ ~
Unexpected token ')' in expression or statement.
At line:9 char:53
+ $ipparts.$startnum = [string]::join('.',$ipParts$startnum)
+ ~
Missing ')' in method call.
At line:9 char:53
+ $ipparts.$startnum = [string]::join('.',$ipParts$startnum)
+ ~~~~~~~~~
Unexpected token '$startnum' in expression or statement.
At line:9 char:62
+ $ipparts.$startnum = [string]::join('.',$ipParts$startnum)
+ ~
Unexpected token ')' in expression or statement.
At line:10 char:1
+ }
+ ~
Unexpected token '}' in expression or statement.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : MissingEndParenthesisInMethodCall
There are two parts to this, but i'm hoping if I can figure out this first part then the second part when I use the final value that is supposed to be stored in $ipParts$startnum to do the lookups will be easier.
EDIT
So I had an idea and change the script to this
$iplist = get-content c:\powershell\ips.txt | where-object { $_.Trim() -ne '' }
$startnum = 0
foreach($line in $iplist){
$startnum++
new-variable -name "ip$startnum" -Value $line -Force -ErrorAction SilentlyContinue
new-variable -name "ipParts$startnum" -Value "$ip$startnum".Split('.')
$iptemp = get-variable -name "ipparts$startnum"
[array]::Reverse("$iptemp")
$iptemp = [string]::join('.',"$iptemp")
set-variable -name "ipParts$startnum" -Value "$iptemp"
}
write-host "iplist is $iplist......ip1 is $ip1....ip2 is $ip2....ipparts1 is $ipparts1.......ipparts2 is $ipparts2....iptemp value is $iptemp"
Basically use set-variable to modify it as a string with other variable names in it, kind of works, but when i did write-host on the last part to make sure its actually writing the proper values to the variables, on $ipparts1, $ipparts2, and $iptemp, i have the following value
"System.Management.Automation.PSVariable"
I'm not entirely sure what that means.

Related

How do I write a script to reboot a list of servers, but will EXCLUDE servers I don't want rebooted?

Being new to PowerShell I've been following some of the guidance in these posts to write a script for what's mentioned in the subject.
Here's the script:
Get-Content -Path C:\temp\Domain.txt | Restart-Computer -force | Where-Object { $._Name -notmatch "^(SERVER01)"}
Here's the error:
Restart-Computer : Cannot validate argument on parameter 'ComputerName'. The argument is null or empty. Provide an argument that is not null or empty, and then try the command again.
At line:1 char:40
+ ... et-Content -Path C:\temp\Domain.txt | Restart-Computer -force | Where ...
+ ~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidData: (:PSObject) [Restart-Computer], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.RestartComputerCommand
For reference, the DOMAIN.txt has a list of servers that will periodically change so I want to skip certain servers should they end up on the list.
The error simply means you have an empty line on your text file, you can filter the lines using .Where(..) method and exclude empty or white space lines with the help of [string]::IsNullOrWhiteSpace(..) method. I have changed -notmach for the containment operator -notin.
Note, -notin looks for an exact match within the collection $hostsToExclude.
$hostToExclude = 'server1', 'server2'
(Get-Content -Path C:\temp\Domain.txt).Where({
-not [string]::IsNullOrWhiteSpace($_) -and $_ -notin $hostToExclude
}) | Restart-Computer -Force
It's worth noting that, on your snippet, you're restarting the hosts before filtering the collection. Get-Content should be followed by Where-Object.

how to append output to a CSV file

foreach ( $newfile in $file )
{
$b = Get-CMDeploymentStatus -PackageId $newfile -StatusType Any | select PackageID
Write-Output $b | Export-Csv -Path "C:\Users\PSM-6A1A000000000000\Documents\list.csv"
}
I am giving input to this with an input file which has number of package names listed and then I want to process it in such a way that the output comes one after the other right now I am getting an error as
Export-Csv : Cannot bind argument to parameter 'InputObject' because it is null. At line:16 char:20 + Write-Output $b | Export-Csv -Path "C:\Users\PSM-6A1A000000000000\Documents\lis ... + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidData: (:) [Export-Csv], ParameterBindingValidationException + FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.ExportCsvCommand
Your code is assuming that you will have a result coming back from $b, if it does not though, you'll get an error because you're piping $b, which is null, into Export-CSV.
$null |export-csv c:\temp\1.csv
Export-Csv : Cannot bind argument to parameter 'InputObject' because it is null.
At line:1 char:8
+ $null |export-csv c:\temp\1.csv
+ ~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [Export-Csv], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.ExportCs
You should add a 'Guard Clause' before you try to export.
if ($null -ne $b){
Export-csv -Append -InputObject $b
}
At least this will continue executing. Now your next problem is to determine why $b would be empty...from my experience with CM, I bet you need to specify which property in your $file you need. Maybe that line should read:
$b = Get-CMDeploymentStatus -PackageId $newfile.PackageId -StatusType Any | select PackageID
Since you say "I am giving input to this with an input file which has number of package names listed", but your code uses PackageId..
It looks to me that this file contains a packageId, each on a single line.
Anyway, I don't see the code ever reading this file..
If my assumption about the text file is correct, try:
# read the content of the text file and loop through the lines
# collect the output from Get-CMDeploymentStatus in variable $result
$result = Get-Content -Path 'X:\TheFileWithPackageIds.txt' | ForEach-Object {
# inside the ForEach-Object, the $_ automatic variable represents a single line from the text file
Get-CMDeploymentStatus -PackageId $_ -StatusType Any | select PackageID
}
# output on screen
$result
# write to new CSV file
$result | Export-Csv -Path "C:\Users\PSM-6A1A000000000000\Documents\list.csv" -NoTypeInformation

Powershell Error: missing '=' operator after key in hash literal

I needed to create a script that checks to see if an IIS App Pool for a specific site and its child App Pools are started. (I have a separate script to start "stopped" App Pools as I just want to check if they are stopped) I was able to create the script however when I modified it to format the output better I keep getting this error,
At E:\iis\scripts\svc_pl_fm_app_pool_status.ps1:12 char:6
+ App Pool = $item.Name;
+ ~
Missing '=' operator after key in hash literal.
At E:\iis\scripts\svc_pl_fm_app_pool_status.ps1:7 char:29
+ foreach ($item in $results) {
+ ~
Missing closing '}' in statement block.
+ CategoryInfo : ParserError: (:) [], ParseException
+ FullyQualifiedErrorId : MissingEqualsInHashLiteral
I checked here and Microsoft but I left more confused about the error then when I first saw it. I saw this Missing closing '}' in statement block. so i assumed I was in fact missing one but I checked and I am not. I am not sure if it is a spacing/ indentation issue but I do not know enough about this error message. Here is my script below.
$results = $item = $appPool = $status = $NULL
$status = "1"
import-module WebAdministration
$AppPoolList = #()
$results = Get-ChildItem IIS:\AppPools\* | Where-Object { ($_.Name -like "someAppPool*" -and $_.Name -like "someChildAppPool*" )}
foreach ($item in $results) {
if ($item.State -ne "Started") {$status = "0"}
$AppPoolList += [PSCustomObject]#{
App Pool = $item.Name;
Status = $item.State;
}
}
$AppPoolList | Format-List

I am looking to append windows host file get current dynamic ip and map it to a host name

I am looking to append windows host file get current dynamic ip and map it to a host name irrespective of current ip address.
i am getting below error
===============================================
Add-Content : A positional parameter cannot be found that accepts argument 'hostname1'.
At C:\Users\Opps\Desktop\power\New Text Document.ps1:6 char:3
+ {ac -Encoding UTF8 -value "$($env:windir)\system32\Drivers\etc\hosts ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Add-Content], ParameterBindingException
+ FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell.Commands.AddContentCommand
================================================================================
Script :
$ip=get-WmiObject Win32_NetworkAdapterConfiguration|Where {$_.Ipaddress.length -gt 1}
$ip.ipaddress[0]
$hst = $env:COMPUTERNAME
Set-ExecutionPolicy -ExecutionPolicy Unrestricted If ((Get-Content "$($env:windir)\system32\Drivers\etc\hosts" ) -notcontains "127.0.0.2 hostname1")
{ac -Encoding UTF8 "$($env:windir)\system32\Drivers\etc\hosts" ($ip.ipaddress[0]) ($hst) }
Add-Content is expecting a string as a value hence to change the type we need to encapsulate the value in quotes. To access an objects property e.g. $ip.ipaddress[0] while in quotes, in order for the text to not be treated literally, we must wrap it in brackets with a preceding dollar sign "$(...)" officially known as a subexpression operator (see mklement0's explanaton) . To ensure we are not duplicating an entry we run a quick check for the entry with the if statement only proceeding to add-content if both conditions of the if statement are met
$ip = get-WmiObject Win32_NetworkAdapterConfiguration|Where {$_.Ipaddress.length -gt 1}
$ip.ipaddress[0]
$hst = $env:COMPUTERNAME
$hostfile = Get-Content "$($env:windir)\system32\Drivers\etc\hosts"
Set-ExecutionPolicy -ExecutionPolicy Unrestricted
if ($hostfile -notcontains "127.0.0.2 hostname1" -and
(-not($hostfile -like "$($ip.ipaddress[0]) $hst"))) {
Add-Content -Encoding UTF8 "$($env:windir)\system32\Drivers\etc\hosts" "$($ip.ipaddress[0]) $hst"
}

beginner Powershell Script Error

To load a text file of 3 ip addresses comma separated values, into an array, and then have the contents in the array changed for every 3rd octet of the ip address, and then exported back to a csv or text file.
##load file to an array
$ipFileName="C:\Users\HarmanGrewal\Google Drive\win213\assignment2\IP.txt"
$array1=#()
$array1=Get-Content $ipFileName -Delimiter ","
#now we have the contents in an array
$count=0
foreach($i in $array1){
$array1[$count] = $array1[$count] -replace "\.\d{1}\.",".2."
$count++
}
Get-Content $array1 | export-csv -path "C:\Users\HarmanGrewal\test.txt"
A successful exportation of data to a csv file.
An empty csv file instead.
Get-Content : Cannot find path 'C:\Users\HarmanGrewal\192.168.2.10,' because it does not exist.
At C:\Users\HarmanGrewal\Google Drive\win213\assignment2\assignment2QuestionAnswer1.ps1:13 char:1
+ Get-Content $array1 | export-csv -path "C:\Users\HarmanGrewal\test.tx …
+ ~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (C:\Users\HarmanGrewal\192.168.2.10,:String) [Get-Content], ItemNotFound Exception
+ FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetContentCommand
Get-Content : Cannot find path 'C:\Users\HarmanGrewal\192.168.2.14,' because it does not exist.
At C:\Users\HarmanGrewal\Google Drive\win213\assignment2\assignment2QuestionAnswer1.ps1:13 char:1
+ Get-Content $array1 | export-csv -path "C:\Users\HarmanGrewal\test.tx …
+ ~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (C:\Users\HarmanGrewal\192.168.2.14,:String) [Get-Content], ItemNotFound Exception
+ FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetContentCommand
Get-Content : Illegal characters in path.
At C:\Users\HarmanGrewal\Google Drive\win213\assignment2\assignment2QuestionAnswer1.ps1:13 char:1
+ Get-Content $array1 | export-csv -path "C:\Users\HarmanGrewal\test.tx …
+ ~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (C:\Users\HarmanGrewal\192.168.2.15:String) [Get-Content], ArgumentException
+ FullyQualifiedErrorId : ItemExistsArgumentError,Microsoft.PowerShell.Commands.GetContentCommand
Get-Content : Cannot find path 'C:\Users\HarmanGrewal\192.168.2.15' because it does not exist.
At C:\Users\HarmanGrewal\Google Drive\win213\assignment2\assignment2QuestionAnswer1.ps1:13 char:1
+ Get-Content $array1 | export-csv -path "C:\Users\HarmanGrewal\test.tx …
+ ~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (C:\Users\HarmanGrewal\192.168.2.15:String) [Get-Content], ItemNotFoundException
+ FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetContentCommand
# Read the 3 IP addresses from the input file into an array.
# .TrimEnd() ensures that a trailing newline, if any, is stripped (syntax requires PSv3+)
$ips = (Get-Content $ipFileName -Delimiter ",").TrimEnd()
# Replace single-digit IP octets with fixed value 2,
# join the resulting IPs with ',' again, and write to an output file.
$ips -replace '\.\d{1}\.', '.2.' -join ',' | Set-Content "C:\Users\HarmanGrewal\test.txt"
As for what you tried:
$array1=#()
$array1 = ...
$array1=#() is pointless, because the next line assigns to $array1 again, which means that its RHS determines the data type of $array1, irrespective of the previous =#() assignment;
if the Get-Content command happens to return a single value, $array1 will be a scalar; you could prevent that by enclosing the Get-Content command in #(...), the array-subexpression operator, but in PSv3+ that is generally not necessary, due to its unified handling of scalars and collections.
foreach($i in $array1) enumerates the array elements themselves, where $i is a by-value copy of each array element.
Instead of using a separate $count variable to access the elements by reference via their index in order to update them, PowerShell allows you to simply recreate the array as a whole:
$array1 = foreach ($el in $array1) { $el -replace "\.\d{1}\.",".2." }
or, more concisely, relying on the -replace operator's support for array-valued LHS values:
$array1 = $array1 -replace "\.\d{1}\.",".2."
As Harsh Jaswal's answer points out, Get-Content $array1 mistakenly passes intended file contents, whereas what Get-Content expects are filename arguments to read contents from.
Since the values to output - in array $array1 - are already in memory, you can simply send them through the pipeline directly.
Export-Csv operates on the properties of an object, and since you're supplying string objects that only have a .Length property, all that will be exported is that property, which is not the intent.
In the case at hand you have to write a text file directly, using Set-Content, based on constructing CSV-format strings in memory.
Note that Set-Content uses the system's legacy "ANSI" code page by default; use -Encoding <encodingName> to change that.
The first thing is if you are using foreach here then there is no need of $count variable. And the second thing is in your last line you are passing collection $array1 to Get-Content. It takes the path as a parameter. So it is trying to get the contents of the file stored at $array1, which is not correct.
Please modify the code as below.
$ipFileName="C:\Users\HarmanGrewal\Google Drive\win213\assignment2\IP.txt"
$array1=#()
$array1=Get-Content $ipFileName -Delimiter ","
#now we have the contents in an array
foreach($i in $array1){
$i = $i -replace "\.\d{1}\.",".2."
$i
}
$array1 | export-csv -path C:\Users\HarmanGrewal\test.txt