Apologies all, another newbie question,
I am trying to import a list of firewall rules in order to harden the ESXi hosts. I have copied out the rules in the format below, modified them in excel and saved to a csv file.
I created a function to copy them from a source esxi host and copy them to a destination host. I would now like to do this differently and copy in from a CSV (as we can use this as documentation for each rule) in the same format. The issue I am getting is for the "AllowedIPAddresses" column, this was originally a system.object in the output from powercli but obviously a string when I import it back in.
Ruleset AllowedIPAddresses Enabled activeDirectoryAll {AA.BB.28.1,AA.BB.28.2,AA.BB.124.1,AA.BB.124.2} TRUE CIMHttpServer {AA.BB.134.77} TRUE CIMHttpsServer {AA.BB.134.77} TRUE CIMSLP {AA.BB.21.128/26} TRUE cmmds {All} FALSE dhcp {All} FALSE DHCPv6 {All} FALSE dns {AA.BB.28.1,AA.BB.28.2,AA.BB.124.1,AA.BB.124.2} TRUE DVFilter {All} FALSE DVSSync {AA.BB.136.0/24} TRUE faultTolerance {AA.BB.136.0/24} TRUE fdm {AA.BB.136.0/24} TRUE ftpClient {All} FALSE gdbserver {All} FALSE HBR {All} FALSE httpClient {AA.BB.134.78} TRUE IKED {All} FALSE ipfam {All} FALSE iSCSI {All} FALSE N1KV-Distributed-NetFlow {All} TRUE N1KV-L3-Ctrl {AA.BB.141.1,AA.BB.136.0/24} TRUE N1KV-L3-VNService {AA.BB.141.1} TRUE NFC {AA.BB.134.77,AA.BB.136.0/24} TRUE nfsClient {All} FALSE ntpClient {AA.BB.31.123,AA.BB.127.123} TRUE rabbitmqproxy {All} FALSE rdt {All} FALSE remoteSerialPort {All} FALSE snmp {All} FALSE sshClient {All} FALSE sshServer {AA.BB.134.77,AA.BB.98.41,AA.BB.2.41,AA.BB.21.128/26} TRUE syslog {AA.71.AA.10} TRUE updateManager {AA.BB.134.78} TRUE vMotion {All} TRUE vprobeServer {All} FALSE vpxHeartbeats {AA.BB.134.77} TRUE vsanvp {AA.BB.134.77} TRUE vSPC {All} FALSE vSphereClient {AA.BB.134.77,AA.BB.134.78,AA.BB.21.128/26} TRUE webAccess {All} FALSE WOL {All} TRUE
My question is how to I create the "AllowedIPAddresses" as a system.object when I import the {IP1, IP2, IP3} back in and I can compare and do foreach loops again, against the destination hosts settings.
The function is below. I am sure there is a better way of doing what I am doing but I understand this way
# $SrcVIHost = 'Host1'
$DstVIHost = 'Host2'
$vCenterServer = '127.0.0.1'
$applychanges = 'yes'
$SrcFWFile = 'Rules.csv'
Connect-VIServer -Server $vCenterServer | Out-Null
Function Copy-FwSettings {
param
(
[Object]
$CurrentSetting
)
# *** old code to copy settings from host ***
# $SrcEsxcli = Get-EsxCli -VMHost $SrcVIHost
# if($SrcEsxcli -ne $null){
# $Srcfwenabled = $SrcEsxcli.network.firewall.ruleset.list() | Where-Object { $_.Name -eq $CurrentSetting } | Select-Object Name, Enabled
# $SrcfwIpList = $Srcesxcli.network.firewall.ruleset.allowedip.list($CurrentSetting) | Select-Object Ruleset, AllowedIPAddresses
# }
# Copy settings from CSV file
$Srcdata | Select-Object Ruleset, IPAllowed, Enabled
if($Srcdata -ne $null){
$Srcfwenabled = $Srcdata | where-object { $_.Ruleset -eq $CurrentSetting.ruleset } | Select-Object Ruleset, Enabled
$SrcfwIpList = $Srcdata | where-object { $_.Ruleset -eq $CurrentSetting.ruleset } |Select-Object Ruleset, IPAllowedIPAddresses
}
# Get destination host current settings.
$DstEsxcli = Get-EsxCli -VMHost $DstVIHost
if($DstEsxcli -ne $null){
$Dstfwenabled = $DstEsxcli.network.firewall.ruleset.list() | Where-Object { $_.Name -eq $CurrentSetting } | Select-Object Name, Enabled
$DstfwIpList = $Dstesxcli.network.firewall.ruleset.allowedip.list($CurrentSetting) | Select-Object Ruleset, AllowedIPAddresses
}
# Compare Firewall rulesets for a difference
$diff = Compare-Object -DifferenceObject $SrcfwIpList -ReferenceObject $DstfwIpList -Property AllowedIpAddresses
#Checking if FW rule $CurrentSetting should be enabled for all IP Addresses...
If (($Srcfwenabled.enabled -eq $Dstfwenabled.enabled) -and ($Srcfwenabled.enabled -eq $true) -and ($SrcfwIpList.AllowedIPAddresses -eq 'All') -and ($DestfwIpList.AllowedIPAddresses -eq 'All')) {
write-host "The $CurrentSetting rule is already enabled for all IP Addresses" -ForegroundColor Green
}
#Checking if FW rule $CurrentSetting is disabled on both systems...
elseif (($Srcfwenabled.enabled -eq $false) -and ($Dstfwenabled.enabled -eq $false)) {
Write-host "The rule $CurrentSetting is already disabled"
}
#Write-Host "Checking if FW rule $CurrentSetting should be disabled on the destination Host...
elseif (($Srcfwenabled.enabled -eq $false) -and ($Dstfwenabled.enabled -eq $true)) {
write-host "The $CurrentSetting needs to be disabled on the destination host"
if ($ApplyChanges -eq 'Yes') {
$DstEsxCli.network.firewall.ruleset.set($true, $false, $CurrentSetting) # Sets “AllowedAll” to $true, “Enabled” to $false on rule set defined by function
$DstEsxcli.network.firewall.refresh() # Reloads the firewall rules
}
else {
write-Host 'Changes Disabled not Disabling firewall Rule' -foregroundcolor 'Yellow'
}
}
#Checking if FW rule $CurrentSetting should be enabled on the destination Host and allow all IP's...
elseif (($Srcfwenabled.enabled -eq $true) -and ($Dstfwenabled.enabled -eq $false)) {
write-host "The $CurrentSetting needs to be enabled on the destination host and allow all IP Addresses"
if ($ApplyChanges -eq 'Yes') {
$DstEsxCli.network.firewall.ruleset.set($true, $true, $CurrentSetting) # Sets “AllowedAll” to $true, “Enabled” to $true on rule set defined by function
$DstEsxcli.network.firewall.refresh() # Reloads the firewall rules
}
else {
write-Host 'Changes Disabled not enabling firewall Rule' -foregroundcolor 'Yellow'
}
}
# Checking if FW rule $CurrentSetting should be enabled on the destination Host and allow specific IP's...
elseif (($Srcfwenabled.enabled -eq $true) -and ($Srcfwenabled.enabled -eq $true) -and ($SrcfwIpList.AllowedIPAddresses -ne 'All')) {
if ($DstfwIpList.AllowedIPAddresses -eq 'All') {
write-host "The $CurrentSetting needs to be enabled on the destination host and allow specific IP Addresses"
if ($ApplyChanges -eq 'Yes') {
Write-Host 'Setting firewall ruleset'
$DstEsxCli.network.firewall.ruleset.set($false, $true, $CurrentSetting) # Sets “AllowedAll” to $false, “Enabled” to $true on rule set defined by function
$DstEsxcli.network.firewall.refresh() # Reloads the firewall rules
}
else {
Write-Host "Changes disabled not changing Firewall config for $CurrentSetting" -foregroundcolor 'Yellow'
}
}
else {
write-host "The $CurrentSetting is already enabled on the destination host and allow specific IP Addresses" -ForegroundColor 'Green'
}
If ($diff -eq $null) {
Write-Host "The rule $CurrentSetting is enabled and configured correctly and the Allowed IP's are already correct" -ForegroundColor 'Green'
}
else {
if ($ApplyChanges -eq 'Yes') {
# Removing Old IP Addresses.
If ($DstfwIpList.AllowedIPAddresses -ne 'All'){
foreach ($DIP in $DstfwIpList.AllowedIPAddresses) {
$DstEsxCli.network.firewall.ruleset.allowedip.remove("$DIP", "$CurrentSetting")
Write-Host "$CurrentSetting Allowed IP Addresses Changed. Removed $DIP"
}
}
# Adding IP Addresses.
Write-Host 'Adding the correct IP Addresses'
foreach ($SIP in $SrcfwIpList.AllowedIPAddresses) {
$DstEsxCli.network.firewall.ruleset.allowedip.add("$SIP", "$CurrentSetting")
Write-Host "$CurrentSetting Allowed IP Addresses Changed. Added $SIP"
}
}
else {
write-Host 'Changes Disabled not applying' -foregroundcolor 'Yellow'
}
$DstEsxcli.network.firewall.refresh() # Reloads the firewall rules
}
}
else {
Write-Host 'There was an issue with FW configuration' -ForegroundColor 'Red'
}
}
$Srcdata = import-csv -path $SrcFWFile
Foreach ($CurrentSetting in $Scrdata) {
Copy-FwSettings $CurrentSetting.Ruleset
}
Many thanks for any ideas or answers to give me some clues.
Buzz
Back to the start
Is there a specific reason to use ESXCLI command instead of the API?
Please check the HostFirewallSystem
In general it is better to use the API and use esxcli only if something is not available via the API.
If you want to continue with esxcli
$DstEsxCli.network.firewall.ruleset.set($true, $false, $CurrentSetting)
$esxcli.network.firewall.ruleset.set($allowedall,$enabled,$rulesetid)
.PARAMETER allowedall
Set to true to allowed all ip, set to false to use allowed ip list.
.PARAMETER enabled
Set to true to enable ruleset, set to false to disable it.
.PARAMETER rulesetid
The label of the ruleset.
[System.Nullable[boolean]]$allowedall,
[System.Nullable[boolean]]$enabled,
[string]$rulesetid,
$DstEsxCli.network.firewall.ruleset.allowedip.remove("$DIP", "$CurrentSetting")
$esxcli.network.firewall.ruleset.allowedip.remove($ipaddress,$rulesetid)
.PARAMETER ipaddress
Allowed ip address/range for the ruleset.
.PARAMETER rulesetid
The label of the ruleset.
[string]$ipaddress,
[string]$rulesetid,
$DstEsxCli.network.firewall.ruleset.allowedip.add("$SIP", "$CurrentSetting")
$esxcli.network.firewall.ruleset.allowedip.add($ipaddress,$rulesetid)
.PARAMETER ipaddress
Allowed ip address/range for the ruleset.
.PARAMETER rulesetid
The label of the ruleset.
[string]$ipaddress,
[string]$rulesetid,
All parameters above have been extracted from get-esxli on steroids and these functions have been generated from information directly obtained from get-esxcli for all namespaces.
The key point is that none of these functions require as a parameter an object of type:
TypeName: "Selected.VMware.VimAutomation.ViCore.Impl.V1.EsxCli.EsxCliObjectImpl"
So you don't need to convert back to a "Selected.VMware.VimAutomation.ViCore.Impl.V1.EsxCli.EsxCliObjectImpl" consequently the title of this topic is not relevant anymore.
And by the way this type of object will be returned for ANY get-esxcli command.
If you need to split your initial csv table in many lines (One for each IP) you can use
$MyTable = Import-csv "YourPath\firewall.csv" -Delimiter `t
$NewTable = $MyTable | foreach-object{
$MyRow = $_
($_.ALLowedIPAddresses -replace'[{}]','').split(",") | foreach-object{
$Output = New-Object -Type PSObject -Prop ([ordered]@{
'RuleSet'= $MyRow.RuleSet
'ALLowedIPAddresses' = $_
'Enabled' = $MyRow.Enabled
})
Return $Output
}
}
$NewTable
The result is:
PowerCLI C:\> $NewTable
RuleSet ALLowedIPAddresses Enabled
------- ------------------ -------
activeDirectoryAll AA.BB.28.1 TRUE
activeDirectoryAll AA.BB.28.2 TRUE
activeDirectoryAll AA.BB.124.1 TRUE
activeDirectoryAll AA.BB.124.2 TRUE
CIMHttpServer AA.BB.134.77 TRUE
CIMHttpsServer AA.BB.134.77 TRUE
CIMSLP AA.BB.21.128/26 TRUE
cmmds All FALSE
****
Fot the import i have used tab as a delimiter. (Tab was used in the example you have provided)
-replace'[{}]','
is used to remove the characters{}
.split(",")
As discussed earlier
If and only if "activeDirectoryAll" is the ruleset ID (I am not able to test) then you should be in position to do for example:
$NewTable | foreach-object {
$DstEsxCli.network.firewall.ruleset.allowedip.add($_.ALLowedIPAddresses, "$_.RuleSet")
}
Or if you just want an object with an array in "AllowedIPAddresses"
$NewTable = $MyTable | foreach-object{
$Output = New-Object -Type PSObject -Prop ([ordered]@{
'RuleSet'= $_.RuleSet
'ALLowedIPAddresses' = ($_.ALLowedIPAddresses -replace'[{}]','').split(",")
'Enabled' = $_.Enabled
})
Return $Output
}
$NewTable
$NewTable and $MyTable will look the same...
But
$MyTable.allowedIPAddresses
$NewTable.allowedIPAddresses
Will show that you have now an array within ALLowedIPAddresses and not a long string.
"AllowedIPAddresses" as a system.object
Maybe the split function will help in that case (however maybe it will not create a system.object)
Using it on the AllowedIPAddresses with "," as a separator.
Thanks for the reply I really appreciate it.
Your reply got me thinking. I don't know whether it matters if its a system.object or not, aren't they both arrays?
I have simplified what I am trying to do, by removing the full function, to its bare minimum. I **think** I need to somehow make the AllowedIPAddresses an array rather than string. this is basically what I am struggling with
$SrcFWFile = 'c:\temp\Rules.csv'
$Srcdata = import-csv -path $SrcFWFile
# $Srcdata | Select-Object Ruleset, AllowedIPAddresses , Enabled
$Srcdata = import-csv -path $SrcFWFile
foreach ($currentsetting in $Srcdata) {
if($Srcdata -ne $null){
$Srcfwenabled = $Srcdata | Where-Object { $_.Ruleset -eq $CurrentSetting.Ruleset } | Select-Object Ruleset, Enabled
$SrcfwIpList = $Srcdata | Where-Object { $_.Ruleset -eq $CurrentSetting.Ruleset } | Select-Object Ruleset, AllowedIPAddresses
}
$type = $SrcfwIpList.AllowedIPAddresses
$SrcfwIpList.Ruleset, $type.GetType().Name, $SrcfwIpList.AllowedIPAddresses
}
I changed the code slightly to display the member types
# Load Windows PowerShell cmdlets for managing vSphere
Add-PsSnapin VMware.VimAutomation.Core -ea 'SilentlyContinue'
$DstVIHost = 'Host2'
$applychanges = 'yes'
$SrcFWFile = 'c:\temp\Rules.csv'
$vCenterServer = '127.0.0.1'
$applychanges = 'yes'
$CurrentSetting = 'sshServer'
Connect-VIServer -Server $vCenterServer | Out-Null
$DstEsxcli = Get-EsxCli -VMHost $DstVIHost
if($DstEsxcli -ne $null){
$Dstfwenabled = $DstEsxcli.network.firewall.ruleset.list() | Where-Object { $_.Name -eq $CurrentSetting } | Select-Object Name, Enabled
$DstfwIpList = $Dstesxcli.network.firewall.ruleset.allowedip.list($CurrentSetting) | Select-Object Ruleset, AllowedIPAddresses
}
$Srcdata = import-csv -path $SrcFWFile
# $Srcdata | Select-Object Ruleset, AllowedIPAddresses , Enabled
$Srcdata = @(import-csv -path $SrcFWFile)
foreach ($currentsetting in $Srcdata) {
if($Srcdata -ne $null){
$Srcfwenabled = $Srcdata | Where-Object { $_.Ruleset -eq $CurrentSetting.Ruleset } | Select-Object Ruleset, Enabled
$SrcfwIpList = $Srcdata | Where-Object { $_.Ruleset -eq $CurrentSetting.Ruleset } | Select-Object Ruleset, AllowedIPAddresses
}
}
$SrcfwIpList | get-Member
$DstfwIpList | Get-Member
This gives the output
TypeName: Selected.System.Management.Automation.PSCustomObject
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
AllowedIPAddresses NoteProperty System.String AllowedIPAddresses={All}
Ruleset NoteProperty System.String Ruleset=WOL
TypeName: Selected.VMware.VimAutomation.ViCore.Impl.V1.EsxCli.EsxCliObjectImpl
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
AllowedIPAddresses NoteProperty System.Object[] AllowedIPAddresses=System.Object[]
Ruleset NoteProperty System.String Ruleset=sshServer
I am now fairly sure that I was partially correct with my original statement when I suggested import the "AllowedIPAddresses" as a system.object. Unfortunately, I am only a white belt at powershell and a little lost with regards to using split or another method to manipulate the string {IP1, IP2, IP3} and make them usable within the array.
Thanks
Back to the start
Is there a specific reason to use ESXCLI command instead of the API?
Please check the HostFirewallSystem
In general it is better to use the API and use esxcli only if something is not available via the API.
If you want to continue with esxcli
$DstEsxCli.network.firewall.ruleset.set($true, $false, $CurrentSetting)
$esxcli.network.firewall.ruleset.set($allowedall,$enabled,$rulesetid)
.PARAMETER allowedall
Set to true to allowed all ip, set to false to use allowed ip list.
.PARAMETER enabled
Set to true to enable ruleset, set to false to disable it.
.PARAMETER rulesetid
The label of the ruleset.
[System.Nullable[boolean]]$allowedall,
[System.Nullable[boolean]]$enabled,
[string]$rulesetid,
$DstEsxCli.network.firewall.ruleset.allowedip.remove("$DIP", "$CurrentSetting")
$esxcli.network.firewall.ruleset.allowedip.remove($ipaddress,$rulesetid)
.PARAMETER ipaddress
Allowed ip address/range for the ruleset.
.PARAMETER rulesetid
The label of the ruleset.
[string]$ipaddress,
[string]$rulesetid,
$DstEsxCli.network.firewall.ruleset.allowedip.add("$SIP", "$CurrentSetting")
$esxcli.network.firewall.ruleset.allowedip.add($ipaddress,$rulesetid)
.PARAMETER ipaddress
Allowed ip address/range for the ruleset.
.PARAMETER rulesetid
The label of the ruleset.
[string]$ipaddress,
[string]$rulesetid,
All parameters above have been extracted from get-esxli on steroids and these functions have been generated from information directly obtained from get-esxcli for all namespaces.
The key point is that none of these functions require as a parameter an object of type:
TypeName: "Selected.VMware.VimAutomation.ViCore.Impl.V1.EsxCli.EsxCliObjectImpl"
So you don't need to convert back to a "Selected.VMware.VimAutomation.ViCore.Impl.V1.EsxCli.EsxCliObjectImpl" consequently the title of this topic is not relevant anymore.
And by the way this type of object will be returned for ANY get-esxcli command.
If you need to split your initial csv table in many lines (One for each IP) you can use
$MyTable = Import-csv "YourPath\firewall.csv" -Delimiter `t
$NewTable = $MyTable | foreach-object{
$MyRow = $_
($_.ALLowedIPAddresses -replace'[{}]','').split(",") | foreach-object{
$Output = New-Object -Type PSObject -Prop ([ordered]@{
'RuleSet'= $MyRow.RuleSet
'ALLowedIPAddresses' = $_
'Enabled' = $MyRow.Enabled
})
Return $Output
}
}
$NewTable
The result is:
PowerCLI C:\> $NewTable
RuleSet ALLowedIPAddresses Enabled
------- ------------------ -------
activeDirectoryAll AA.BB.28.1 TRUE
activeDirectoryAll AA.BB.28.2 TRUE
activeDirectoryAll AA.BB.124.1 TRUE
activeDirectoryAll AA.BB.124.2 TRUE
CIMHttpServer AA.BB.134.77 TRUE
CIMHttpsServer AA.BB.134.77 TRUE
CIMSLP AA.BB.21.128/26 TRUE
cmmds All FALSE
****
Fot the import i have used tab as a delimiter. (Tab was used in the example you have provided)
-replace'[{}]','
is used to remove the characters{}
.split(",")
As discussed earlier
If and only if "activeDirectoryAll" is the ruleset ID (I am not able to test) then you should be in position to do for example:
$NewTable | foreach-object {
$DstEsxCli.network.firewall.ruleset.allowedip.add($_.ALLowedIPAddresses, "$_.RuleSet")
}
Or if you just want an object with an array in "AllowedIPAddresses"
$NewTable = $MyTable | foreach-object{
$Output = New-Object -Type PSObject -Prop ([ordered]@{
'RuleSet'= $_.RuleSet
'ALLowedIPAddresses' = ($_.ALLowedIPAddresses -replace'[{}]','').split(",")
'Enabled' = $_.Enabled
})
Return $Output
}
$NewTable
$NewTable and $MyTable will look the same...
But
$MyTable.allowedIPAddresses
$NewTable.allowedIPAddresses
Will show that you have now an array within ALLowedIPAddresses and not a long string.
Thanks very much helped me out a lot