IF Statement to Verify VLAN Exists in PowerCLI Script - powershell

I am writing a PowerCLI script to automate the creation of VMs based on the data within a CSV file and I would like to know how to format an IF statement to check if the VLANs specified already exist to avoid cluttering up the screen with errors.
The section of the script dealing with the VLAN creation in its current format:
New-VM -Name $_.Name -VMHost ($esx | Get-Random) -NumCPU $_.NumCPU -Location $Folder
$list = Get-Cluster $_.Cluster | Get-VMHost
foreach ($esxhost in $list)
{ Get-VirtualSwitch -Name $switch -VMHost $esxhost |
New-VirtualPortgroup -Name "VLAN $($_.VLAN)" -VLANID $($_.VLAN)
}
Write-Host "Wait - propagating VLAN $($_.VLAN) to all hosts" -foreground yellow
Start-Sleep 10
I would like to determine a way to have the script do something like:
IF $_.VLAN exists
Write-host "$_.VLAN already present, proceeding to next step"
ELSE DO{ Get-VirtualSwitch -Name $switch -VMHost $esxhost |
New-VirtualPortgroup -Name "VLAN $($_.VLAN)" -VLANID $($_.VLAN)
}
I don't have much experience in writing these so I was hoping for some assistance on how to
Check whether the VLAN already exists in vSphere on the switch
How to format the IF/ELSE statement properly to avoid cluttering up the PowerCLI window with errors when the script is run
Thank you for any assistance you may provide

EDIT to work for vlan rather than vswitch
You could use get-virtualportgroup for this and check if the names returned contain your vlanid. This won't work for distributed switches as that's a different set of cmdlets.
$host = 'YourHost'
$vlanid = 'YourVlanId'
if ((Get-VirtualPortGroup -host $host).VLanId -contains $vlanid )
{
Write-Output 'vlan present'
}
else
{
Write-Output 'vlan missing'
#your code to create vlan here
}

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.

While Loop with Break Statement in PowerShell [duplicate]

I am trying to build my own script to check some Windows services (status and start mode) and I am facing an issue on the IF ...
For example even if the service is "Running", it will never run the code inside the IF...
let me share my code below (I am a newbie on powershell so be gentle xD)
For info, I will do more actions inside the IF and ELSE, it is just for the example.
# import computers list, 1 by line
$Computers = get-content .\computers.txt
# define variable of services we want to check
$ServiceNetbios = "netbt"
# define variable to ask credentials
$Cred = Get-Credential
# declare Function to open a session a remote computer
Function EndPSS { Get-PSSession | Remove-PSSession }
EndPSS
########################################################
# BEGINNING OF SCRIPT #
# by xxx #
# 2022-02-03 #
########################################################
# loop for each computer imported from the file
foreach ($computer in $computers) {
# show name of computer in progress
$computer
# connect remotely to the computer
$session = New-PSSession -ComputerName $computer -Credential $Cred
# check Netbios service
$StatusServiceNetbios = Invoke-Command -Session $session -ScriptBlock { Get-Service -Name $Using:ServiceNetbios | select -property * }
# Check Netbios service started or not
write-host $StatusServiceNetbios.Status
if ($StatusServiceNetbios.Status -eq 'Running')
{
Write-host "IF Running"
}
else
{
write-host "IF NOT Running"
}
EndPSS
}
and what return my script :
computername
Running (<= the variable $StatusServiceNetbios.Status )
IF NOT Running (<= the ELSE action)
Thanks you in advance for your help,
this drive me crazy and maybe this is very simple...
To complement Cpt.Whale's helpful answer, this is likely to be caused by the serialization and deserialization done by Invoke-Command:
using namespace System.Management.Automation
$service = Get-Service netbt
$afterInvokeCmd = [PSSerializer]::Deserialize(([PSSerializer]::Serialize($service)))
$service.Status -eq 'Running' # => True
$afterInvokeCmd.Status -eq 'Running' # => False
$afterInvokeCmd.Status.Value -eq 'Running' # => True
$afterInvokeCmd.Status.ToString() -eq 'Running' # => True
To put some context to my answer, this is a nice quote from about_Remote_Output that can better explain why and what is happening:
Because most live Microsoft .NET Framework objects (such as the objects that PowerShell cmdlets return) cannot be transmitted over the network, the live objects are "serialized". In other words, the live objects are converted into XML representations of the object and its properties. Then, the XML-based serialized object is transmitted across the network.
On the local computer, PowerShell receives the XML-based serialized object and "deserializes" it by converting the XML-based object into a standard .NET Framework object.
However, the deserialized object is not a live object. It is a snapshot of the object at the time that it was serialized, and it includes properties but no methods.
This is probably because of the way powershell creates service objects - (Get-Service netbt).Status has a child property named Value:
$StatusServiceNetbios.Status
Value
-----
Running
# so Status is never -eq to 'Running':
$StatusServiceNetbios.Status -eq 'Running'
False
# use the Value property in your If statement instead:
$StatusServiceNetbios.Status.Value -eq 'Running'
True

Is there a way to find every single modules which will be needed in script?

I'd like to use a kinda analyzer which will install/import all the needed modules by the script before I run it on distant machine (which could not have it) ......
any idea ?
EDIT
Here's the case :
I'm on my dev machine, I'ved already installed lots of modules of all kind (dhcp, ntfs, remoting, register, etc.)
When I finally got my script (which is a function) to work, I can't be sure of what modules are used....
What I want is to write down, in the 'begin' section, the correct imports before I send my script on remote PCs; to be sure it's gonna run perfectly, you follow ?...
Is there a kinda a third party appplication which can scan my script and give me all needed modules ?
You could do something like this to get help in finding commands used and their source/module names. It's very unpolished, just trying to give the idea.
$scriptblock = {
Write-Host "Nothing here"
$files = Get-ChildItem c:\temp
Get-ADUser someuser
Test-NetConnection www.google.com
}
# Uncomment following lines and enter the path to your script file
# $scriptFile = "Path\to\some\scriptfile"
# $scriptblock = [scriptblock]::Create((Get-Content -raw -Path $scriptFile))
$ast = $scriptblock.Ast
$commands = $ast.FindAll( { $args[0] -is [System.Management.Automation.Language.CommandAst] }, $true)
$commandText = foreach ($command in $commands) {
$command.CommandElements[0].Extent.Text
}
$commandText |
Select-Object -Unique |
Sort-Object |
Select-Object #{
Label = "CommandName"
Expression = { $_ }
},
#{
Label = "Source"
Expression = {
(Get-Command $_).Source
}
}
Output
CommandName Source
----------- ------
Get-ADUser ActiveDirectory
Get-ChildItem Microsoft.PowerShell.Management
Test-NetConnection NetTCPIP
Write-Host Microsoft.PowerShell.Utility
Yeah, you could for example test if the module exists on that particular machine by trying to import it as follows
Try {
Import-Module dbaclone -ErrorAction stop
#ErrorAction required as failing to import is not a terminating action
} Catch {
Write-Verbose -Verbose "Failed to find dbaclone module - installing"
Install-Module dbaclone -AllowClobber -Force
Write-Verbose -Verbose "Installed!"
Import-Module dbaclone
}

DNS name from IP address

I need to create a list of IP addresses and DNS names. I
am trying to get DNS names from IP addresses. I have tried two ways:
try/catch but it ends afterwards.
Without and it just outputs DNS names that I can't relate to the IP addresses.
Here's what I have so far:
#try {
Get-Content C:\Users\pintose\Documents\IP_Address.txt | ForEach-Object
{([system.net.dns]::GetHostByAddress($_)).hostname >> C:\Users\user\Documents\hostname.txt}
# }
# catch {
if ($_.Exception.Message -like "*The requested name is valid*") {
Write-Output "UNREACHABLE" | Out-File C:\Users\user\Documents\hostname.txt }
# }
Try this solution:
$outFile = "C:\Users\user\Documents\hostname.txt"
Get-Content C:\Users\pintose\Documents\IP_Address.txt | ForEach-Object {
$hash = #{ IPAddress = $_
hostname = "n/a"
}
$hash.hostname = ([system.net.dns]::GetHostByAddress($_)).hostname
$object = New-Object psobject -Property $hash
Export-CSV -InputObject $object -Path $outFile -Append -NoTypeInformation
}
We create a objects, that have the IPaddress in it and a hostname n/a if it cannot be resolved. Then, the object gets exported into the file. You'll get something like:
192.0.0.1; Server1
This uses a workflow so it can do parallel foreach
Workflow Get-DNSNames([string[]]$IPAddresses){
foreach -parallel ($IP in $IPAddresses){
try{
#{$IP = $(([system.net.dns]::GetHostByAddress($IP)).hostname)}
}catch{
#{$IP = "N/A"}
}
}
}
$List = Get-DNSNames -IPAddresses $(Get-Content "C:\IPAddresses.txt").Split("[\r\n]")
$List | Out-File "C:\IPAddresses_Complete.txt"
You might want to try the other solutions offered here, but here are some things you might want to think about.
First, I'd recommend not putting the try{}catch{} around the whole of the first command. If you are looping through data and just one of them causes an exception, you risk not completing the task. Put the try{}catch{} around just the "risky" line of code:
Get-Content C:\Users\pintose\Documents\IP_Address.txt | Foreach-Object {
try {
([system.net.dns]::GetHostByAddress($_)).hostname >> C:\Users\user\Documents\hostname.txt
}
catch {
if ($_.Exception.Message -like "*The requested name is valid*") {
Write-Output "UNREACHABLE" | Out-File C:\Users\user\Documents\hostname.txt
}
}
}
When you catch the exception, you only write to the text file in the case that "the requested name is valid" (do you mean invalid?). You never write anything to the file otherwise. Thus, going back to your original code:
IF there is an exception caused by ANY of the IP addresses
AND the exception is NOT "the requested name is valid" (which I think might be a typo?)
THEN no error gets written to the file and the script ends without necessarily completing all the IP addresses.
Other things:
You use two methods to write to the file: >> and Out-File. Probably better to use the PowerShell cmdlet but with the -Append switch to ensure you append to the end of the file:
([system.net.dns]::GetHostByAddress($_)).hostname | Out-File C:\Users\user\Documents\hostname.txt -Append
Write-Output "UNREACHABLE" | Out-File C:\Users\user\Documents\hostname.txt -Append
#restless1987 has suggested a way to ensure you write both the IP address and the hostname (if determined) to the output file. I'd have a look at that to work out what is going on.
My final tip would be to be wary of reading in from .txt files with Get-Content. They often have trailing (blank) lines and you might want to try to ignore such blanks. Probably not a big issue in this case as it will just mean a failed DNS attempt, but I have seen such things wreak havoc on every mailbox in a (very) large company when used with other commands.
Another way...
$ip_list = Get-Content ./IP_Address.txt
foreach ($ip in $ip_list) {
try {
([system.net.dns]::GetHostByAddress($ip)).hostname |
Out-File -FilePath ./hostname.txt -Append
}
catch {
if ($_.Exception.Message -like "*The requested name is valid*") {
Write-Output "UNREACHABLE" | Out-File -FilePath './hostname.txt' -Append }
}
}
There are many tools that can accomplish this, but if you need a quick and dirty solution that you can run just about anywhere this will get the job done.
Using the eternally useful ps tools such as psloggedon /accepteula \\computername or ip address you can get who is currently logged in to check if this is the correct machine. For example:
c:\pstools>psloggedon /accepteula \\10.0.0.10
loggedon v1.33 - See who's logged on
Copyright ⌐ 2000-2006 Mark Russinovich
Sysinternals - www.sysinternals.com
Users logged on locally:
Error: could not retrieve logon time
NT AUTHORITY\LOCAL SERVICE
Error: could not retrieve logon time
NT AUTHORITY\NETWORK SERVICE
1/12/2015 8:06:51 AM DOMAIN\user
Error: could not retrieve logon time
NT AUTHORITY\SYSTEM
Users logged on via resource shares:
1/17/2015 2:26:43 PM DOMAIN\user
Now that you have confirmed that this IP address is the correct one for this user. We just need to lookup the IP address using nslookup.
C:\>nslookup 10.0.0.10
Server: server.domain.com
Address: 10.10.0.1
Name: Workstation07.server.domain.com
Address: 10.0.0.10
Now we know that the computer name for that computer is Workstation07.

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