I've been trying to use Powershell with imported PowerCLI commands for VMware administration and I've hit a snag. What I'm trying to do is query all VM's in a location (doesn't matter where), and for every VM I want the group with "Virtual Machine User with Snapshot" permission, and then use that group name to run a Get-ADGroupMembers query for everyone in that group. I also have to remove the domain prefix from the AD query, which would otherwise cause an error.
After some more playing around with outputting hash table information into the csv, as opposed to 'SystemObject[]', I finally got the script so it doesn't return errors EXCEPT on VM's where there is more than one group. It throws an error but the script continues, and just outputs the members of the first group.
How do I get it to do a recursive AD query for every group that is pulled into the owner groups hashtable? The output would be the same as for all the other VM's, just with a line for each group and members.
$AllVMs = #()
$vms = get-vm * -Location datacenter
foreach ($vm in $vms)
{
$owners = Get-VIPermission $vm.name | where-object {$_.role -eq "virtual machine user with snapshot"}
foreach ($owner in $owners)
{
$members = Get-ADGroupMember ($owners.principal -replace '^prefix\\')
$temp = New-Object psobject |
Add-Member Noteproperty "Name" -value $vm.name -PassThru |
Add-Member Noteproperty "Owner" -value (#($owners.principal) -join ',') -PassThru |
Add-Member Noteproperty "Members" -value (#($members.SamAccountName) -join ',') -passthru
$AllVMs+=$temp
}
$AllVMs | Export-Csv -Path c:\users\me\desktop\AllVMs.csv
I was playing around with it some more today and figured it out! I'm running the script right now against a datacenter with 350+ machines so technically I don't know 100% that it works, but it worked against 3 machines :-) I also added a line to list every machine that's owned by more than one group - handy for troubleshooting. Here's the script:
$AllVMs = #()
$vms = get-vm -Location DATACENTER
foreach ($vm in $vms)
{
$owners = #(Get-VIPermission $vm.name | where-object {$_.role -eq "virtual machine user with snapshot"})
if ($owners.count -gt 1) {write-host "** Note ** '$vm' has"$owners.count "owner groups"}
foreach ($owner in $owners)
{
$members = Get-ADGroupMember ($owner.principal -replace '^prefix\\')
$temp = New-Object psobject |
Add-Member Noteproperty "Name" -value $vm.name -PassThru |
Add-Member Noteproperty "Owner" -value (#($owner.principal) -join ',') -PassThru |
Add-Member Noteproperty "Members" -value (#($members.SamAccountName) -join ',') -PassThru
$AllVMs+=$temp
}
}
$AllVMs
Change $owners.principal to $owner.principal after $members = Get-ADGroupMember? In case you have nested AD groups, Get-ADGroupMember has a -Recursive parameter.
Related
I have a file that looks like this
1
Account Policies
1.1
Password Policy
1.1.1
(L1) Ensure 'Enforce password history' is set to '24 or more password(s)' (Automated)
1.1.2
(L1) Ensure 'Maximum password age' is set to '365 or fewer days, but not 0' (Automated)
1.1.3
(L1) Ensure 'Minimum password age' is set to '1 or more day(s)' (Automated)
1.1.4
(L1) Ensure 'Minimum password length' is set to '14 or more character(s)' (Automated)
1.1.5
(L1) Ensure 'Password must meet complexity requirements' is set to 'Enabled' (Automated)
1.1.6
(L1) Ensure 'Store passwords using reversible encryption' is set to 'Disabled' (Automated)
1.2
Account Lockout Policy
1.2.1
(L1) Ensure 'Account lockout duration' is set to '15 or more minute(s)' (Automated)
Im trying to use regex to look for the number patterns and parse the file so my output looks similar to this
1 Account Policies
1.1.1 Enforce password history 24 or more password(s) DC and Member Servers
1.1.2 Maximum password age 365 or fewer days, but not 0 DC and Member Servers
1.1.3 Minimum password age 1 or more day(s) DC and Member Servers
1.1.4 Minimum password length 14 or more character(s) DC and Member Servers
1.1.5 Password must meet complexity requirements Enabled DC and Member Servers
1.1.6 Store passwords using reversible encryption Disabled DC and Member Servers
1.2.1 Account lockout duration 15 or more minute(s) DC and Member Servers
1.2.2 Account lockout threshold 5 or fewer invalid logon attempt(s), but not 0 DC and Member Servers
1.2.3 Reset account lockout counter after 15 or more minute(s) DC and Member Servers
2 Local Policies
The problem is the output doesnt give 1.1 or 2.1. Only 1 and 1.1.1 and 2 and 2.2.1. Im missing a bunch of sections.
Any ideas?
Thank you!
Here is the code I have so far.
$parse=Get-Content '.\raw2.txt'
$allpolicies = #()
foreach($line in $parse){
$server="DC and Member Servers"
if($line -match "^(?<!\S)\d(?!\S)"){
$section=$line
continue
}
if($section -match "^(?<!\S)\d(?!\S)"){
$policy = $line
$setting = ""
$server=""
$obj=New-Object psobject
$obj | Add-Member -MemberType NoteProperty -Name section -Value $section
$obj | Add-Member -MemberType NoteProperty -Name policy -Value $policy
$obj | Add-Member -MemberType NoteProperty -Name Setting -Value $setting
$obj | Add-Member -MemberType NoteProperty -Name server -Value $server
$allpolicies += $obj
$section=$null
$policy=""
$Setting = ""
continue
}
if(($line -match "\d{1,}.\d{1,}.\d{1,}")){
$section=$line
}
if(($line -match "(L1)") -or ($line -match "(L2)")){
$splitline=$line -split ("'")
$policy=$splitline[1]
$setting=$splitline[3]
if($line -match "(DC only)"){
$server="DC Only"
}
if($line -match "(MS only)"){
$server="MemberServer Only"
}
$obj=New-Object psobject
$obj | Add-Member -MemberType NoteProperty -Name section -Value $section
$obj | Add-Member -MemberType NoteProperty -Name policy -Value $policy
$obj | Add-Member -MemberType NoteProperty -Name Setting -Value $setting
$obj | Add-Member -MemberType NoteProperty -Name server -Value $server
$allpolicies += $obj
$section=$null
}
}
$allpolicies | Out-GridView
I'm trying to pull several region such as us-west-1, us-west-2 or everything security groups from aws but it doesn't seem to be working and it is getting the region that was defined in ./config
is there a way to pull it from all region? and how should i do it?
that is what i have done so far.
Import-Module AWSPowershell
> $EC2GroupList = Get-EC2SecurityGroup | Select-Object Description, GroupId, GroupName,
> IpPermission, IpPermissionsEgress, VpcId
>
> $ChangeList = #()
>
> $GroupsCount = ($EC2GroupList | Measure-Object).Count
> foreach($SecGroup in $EC2GroupList) {
> $Description = $SecGroup.IpPermission.Ipv4Ranges.Description
> $GroupID = $SecGroup.GroupId
> $SecurityGroupName = $SecGroup.GroupName
> $SourceIP = $SecGroup.IpPermission.Ipv4Ranges.CidrIp
> $FromPort = $SecGroup.IpPermission.FromPort
> $ToPort = $SecGroup.IpPermission.ToPort
> $Protocol = $SecGroup.IpPermission.IpProtocol
>
> echo $GroupsCount
>
> if($FromPort -ne $null) {
> $NewObject = new-object -TypeName PSobject
> #$NewObject | Add-Member NoteProperty -Name Region -Value
> $NewObject | Add-Member NoteProperty -Name Description -Value $Description
> $NewObject | Add-Member NoteProperty -Name GroupID -Value $GroupID
> $NewObject | Add-Member NoteProperty -Name SecurityGroupName -Value $SecurityGroupName
> $NewObject | Add-Member NoteProperty -Name SourceIP -Value $SourceIP
> $NewObject | Add-Member NoteProperty -Name FromPort -Value $FromPort
> $NewObject | Add-Member NoteProperty -Name ToPort -Value $ToPort
> $NewObject | Add-Member NoteProperty -Name Protocol -Value $Protocol
> $ChangeList += $NewObject
> }
> }
>
> echo $ChangeList
The API calls are mostly region specific except for a few global services so you need make a call per region generally. You can override the region like this: Get-EC2SecurityGroup -region eu-west-2.
Heres a rough sample
# wrap up your existing code in a function for later...
function Get-RegionSecurityGroupReport{
Param([String] $region = 'eu-west-1');
# replace with your code but pass $region when calling any APIs e.g.
$EC2GroupList = Get-EC2SecurityGroup -region $region | Select-Object ...
# return current regions data
return $EC2GroupList;
}
# Setup the script
$activeRegions = #('eu-west-1', 'eu-west-2');
$results = #();
# loop through regions and execute your function with each region in turn...
$activeRegions | % {
Write-Host "Processing region $($_)";
$results += Get-RegionSecurityGroupReport -region $_;
}
# your multi regional report:
Write-Host $results
There is an API call DescribeRegions gets you all the regions available and the corresponding aws cli call describe-regions.
I found this blog post aws-regions-and-windows-powershell and the corresponding call Get-EC2Region.
I have defined all the needed variables in a separate Variable_Defination.ps1 file and have called it up here.
Is there a better way to do this?
I just want a script to create 2 redundant VMs both running on separate datastores and having Affinity rule set up for disaster recovery cases
. ./Variable_Defination.ps1
function CreateRedundantVMs {
Param(
[Parameter(Mandatory=$true, ValueFromPipeline=$true)]
[string]$ClusterName,
[string]$ComputerName1,
[string]$ComputerName2,
[string]$sourcetemplate,
[string]$InfraResourcePoolName,
[string]$OSCustomizationspec,
[string]$description
)
$Viabledatastores = (Get-Cluster $ClusterName | Get-Datastore) |
Where {$_.Name -like '*vSSD*'} |
Sort-Object FreeSpaceGB -Descending |
Select -First 2 -ErrorAction 'Stop'
if ($Viabledatastores) {
$Viabledatastores
} else {
Write-Host "No Viable Datastores found"
break;
}
Write-Verbose "`n---------------------- Creating redundant VMs now ------------------------`n " -Verbose
New-VM -Name $ComputerName1 -ResourcePool $InfraResourcePoolName -Datastore $Viabledatastores[0] -Description $description -Template $sourcetemplate -OSCustomizationspec $OSCustomizationspec -DiskStorageFormat Thin
New-VM -Name $ComputerName2 -ResourcePool $InfraResourcePoolName -Datastore $Viabledatastores[1] -Description $description -Template $sourcetemplate -OSCustomizationspec $OSCustomizationspec -DiskStorageFormat Thin
Start-Sleep -s 3
Write-Host "`n -------------------------------------------------------------------------`n "
Write-Verbose -Message "Virtual Machine $ComputerName1 and $ComputerName2 Deployed. Powering On" -Verbose
Write-Host "`n -------------------------------------------------------------------------`n "
Start-VM -VM $ComputerName1
Start-VM -VM $ComputerName2
}
# calling function
CreateRedundantVMs $ClusterName $ComputerName1 $ComputerName2 $sourcetemplate $InfraResourcePoolName $OSCustomizationspec $description
I am able to get the free space as ouptut by using the following code.
$body +=echo "------------Free space on Datastore.--------------"`r`n""`r`n""
$body +=get-datastore -name *datastore1* | sort Name | ForEach-Object {
$output=New-Object -TypeName PSObject -Property #{
Freespace = "$([math]::Round($_.FreeSpaceGB, 2)) GB"
Datastore_Name = $_.Name
}
}
Write-Output $output
Is it possible to raise a ticket if the free space is less than 2 GB? If so, how should I change my code?
EDIT :
if (get-datastore | where {$_.FreeSpaceGB -lt 2}){"dosomething"}
or
foreach ($ds in (get-datastore | where {$_.FreeSpaceGB -lt 2})){"dosomething"}
I am trying to work with NETSH from PowerShell. I want see a result from this command such as an object, but netsh returns a string:
netsh wlan show hostednetwork | Get-Member
TypeName: System.String
...
My script must work on system with rather localization, and I can't use -match for parsing a string to an object directly.
How I can solve my trouble?
$netshResult = Invoke-Command -Computername localhost {netsh int tcp show global}
$result = #{}
$netshObject = New-Object psobject -Property #{
ReceiveSideScalingState = $Null
ChimneyOffloadState = $Null
NetDMAState = $Null
}
$netshResult = $netshResult | Select-String : #break into chunks if colon only
$i = 0
while($i -lt $netshResult.Length){
$line = $netshResult[$i]
$line = $line -split(":")
$line[0] = $line[0].trim()
$line[1] = $line[1].trim()
$result.$($line[0]) = $($line[1])
$i++
}
$netshObject.ReceiveSideScalingState = $result.'Receive-Side Scaling State'
$netshObject.ChimneyOffloadState = $result.'Chimney Offload State'
$netshObject.NetDMAState = $result.'NetDMA State'
You got a few alternatives, none of which are nice.
1) Read the netsh output into a string[] and use a custom record parser to create your own object. That is, look at the output on different locales and find out if, say, Hosted newtork settings is always the first header followed by bunch of - characters. If that's the case, assume that next element in array is Mode and so on. This is very error prone, but usually MS command line tools only translate messages, not their order.
2) Look for .Net API for the same information. There is System.Net.NetworkInformation which contains a bunch of connection things. It's a start, though I am not sure if it has info you need.
3) Failing the previous options, use P/Invoke to call native Win32 API. It's a lot of work, so look for pre-existing wrapper libraries before rolling your own.
I recently wrote a cmdlet to parse arbitrary, multi-line text using regular expressions, called ConvertFrom-Text. (Not a great name, if you ask me, but it conforms to the PowerShell naming rules; suggestions are welcome!) So assuming you have that cmdlet, here is one possible solution to your question. (Caveat emptor! The regular expression given was derived from a very small sample of netsh output, so may need some tuning.)
$regex = [regex] '(?ms)(?:^\s*$\s*)?^(?<section>.*?)\s*-+\s*(?<data>.*?)\s*^\s*$'
$result = netsh wlan show hostednetwork | Out-String |
ConvertFrom-Text -pattern $regex -multiline
$result | % {
$dataObj = [PsCustomObject]#{}
$_.Data -split "`r`n" | % {
$element = $_ -split '\s*:\s*'
Add-Member -InputObject $dataObj -MemberType NoteProperty -Name $element[0].Trim() -Value $element[1].Trim()
}
$_.Data = $dataObj # Replace data text with data object
}
$result
On my test system, netsh wlan show hostednetwork returns this:
Hosted network settings
-----------------------
Mode : Allowed
Settings : <Not configured>
Hosted network status
---------------------
Status : Not available
And the output of the $result variable in the code above yields this:
section data
------- ----
Hosted network settings #{Mode=Allowed; Settings=<Not configured>}
Hosted network status #{Status=Not available}
So $result is an array of objects with section and data properties, and the latter is an object with properties defined by the output of the netsh command.
Of course, the above does not get you very far without the ConvertFrom-Text cmdlet. So here is the implementation. (I have copious documentation and examples for it, which will be publicly available once I eventually add it to my open-source PowerShell library.)
filter ConvertFrom-Text
{
[CmdletBinding()]
Param (
[Parameter(Mandatory=$true,Position=0, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
[string[]]$InputObject,
[Parameter(Mandatory=$true,Position=1)]
[regex]$Pattern,
[switch]$RequireAll,
[switch]$Multiline
)
if ($Multiline) {
$dataString = $InputObject -join "`n"
IterateByMatch $dataString $Pattern
}
else {
IterateByLine $InputObject $Pattern
}
}
function IterateByLine([string[]]$data, [regex]$regex)
{
$data | ForEach-Object {
if ($PSItem -match $regex)
{
New-Object PSObject -Property (GetRegexNamedGroups $matches)
}
elseif ($RequireAll) {
throw "invalid line: $_"
}
}
}
function IterateByMatch([string[]]$data, [regex]$regex)
{
$regex.matches($data) | Foreach-Object {
$match = $_
$obj = new-object object
$regex.GetGroupNames() |
Where-Object {$_ -notmatch '^\d+$'} |
Foreach-Object {
Add-Member -InputObject $obj NoteProperty `
$_ $match.groups[$regex.GroupNumberFromName($_)].value
}
$obj
}
}
function Get-RegexNamedGroups($hash)
{
$newHash = #{};
$hash.keys | ? { $_ -notmatch '^\d+$' } | % { $newHash[$_] = $hash[$_] }
$newHash
}