Powershell group export

I needed to get a list of people in som AD group for an audit so I wrote a quick script to export each group matching my filter to a CSV-file and populate it with name and samaccountname. Setting semi colon as an delimiter ensures that you can open the CSV-file in Excel with no additional work to get columns correct.

$groups = Get-ADGroup -filter { name -like "Company-Fileserver-ACL*" }

foreach ($group in $groups)
	Write-Output $group.name
	$file=$group.name + ".csv"
	Get-ADGroupMember $group.name | Select-Object name, samaccountname | Export-Csv -path $file -NoTypeInformation -delimiter ";"

Setup AWX Vcenter inventory with tags part 1

New to AWX and I had a goal to setup Vcenter as an inventory source with groups based on vmware tags. I got that setup working with ansible and started to investigate how to achieve same result in AWX. After a couple of days testing I got some hints on Reddit and was able to get it working as expected. Hopefully this guide can help someone (and me next time) setup inventory with tags.

First step if not already done is to install AWX. I will not cover the setup. It is already covered here https://github.com/ansible/awx/blob/devel/INSTALL.md. I have chosen to install on a standalone Docker host in my Home lab running CentOS.

Open the inventory file install/inventory in your favorite editor.
Look for and uncomment custom_venv_dir=/opt/my-envs/

Create dir: mkdir /opt/my-envs

Run the playbook: ansible-playbook install.yml -i inventory

Create folder and Python env and install all prerequisites

mkdir /opt/my-envs/vm-tags
python3 -m venv /opt/my-envs/vm-tags/
source /opt/my-envs/vm-tags/bin/activate
yum install gcc
yum install python36-devel
pip3 install psutil
pip3 install ansible
pip3 install pyaml
pip3 install requests
pip3 install PyVmomi
pip3 install --upgrade pip setuptools
pip3 install --upgrade git+https://github.com/vmware/vsphere-automation-sdk-python.git

Log on to AWX and navigate to Settings -> System.
Add /opt/my-envs/vm-tags to CUSTOM VIRTUAL ENVIRONMENT PATHS

Last step in this part is to verify our new custom env. Go to ORGANIZATIONS and push then pencil to edit default organization.

Verify that you can see your new Ansible Environment.

Add users or groups to local admin group

Sometimes you need to add users or groups in local Administrators group on a windows server. This function helps to accomplish that on one or more servers. Load a text- or csv-file and pipe it to Add-AdminGroup. All servers not responding will be shown at the end for later follow-up.

function Add-AdminGroup
	Param (
		[parameter(Mandatory = $true,
				   ValueFromPipeline = $true,
				   position = 0)]
		[Alias('IPAddress', '__Server', 'CN', 'server')]
		[Alias('groupname', 'adgroup')]
		if (Test-Connection -quiet -Computername $computername)
			Write-Output "Adding $group to local administrators on" $Computername
			Invoke-Command -ComputerName $Computername -ScriptBlock {
			Add-LocalGroupMember -Group Administrators -Member $args[0]
			} -ArgumentList $group
			write-output "No response from" $Computername
			$failed += $computername
			foreach ($obj in $failed)
				Write-Output $obj

List VMs according to memory and CPU usage

For internal billing purpose I needed a way list all Windows VMs for a given subsidiary and their CPU and memory configuration.

Connect-VIServer -Server vcenter.corp.lan

$var = get-vm -location Subsidiary1 | Where{ $_.Guest.OSFullName -like '*windows*' }  | select numcpu, memorygb | Group-Object numcpu,memorygb

function get-numOfVms
		[parameter(Mandatory = $true)]

	$results = foreach ($row in $var)
		$cpu, $mem = $row.Name -split ',', 2
			NumOfVMs = $row.Count
			NumOfCPUs   = $cpu
			MemoryGB = $mem.Trim()
	return $results
$total = get-numOfVms -VMs $var
$total | Export-Csv -Path totalvms.csv -NoTypeInformation


Example of totalvms.csv. It gives you a number of each specific CPU and memory configuration.


Create DHCP scopes from a CSV file

A fast way to import multiple DHCP scopes to a DHCP server. Some settings needs to be added on top level. For example DNS servers.

Required header in CSV:

$dhcpserver = ""
$scopes = Import-Csv -Path dhcp.csv -Delimiter ";"
foreach ($scope in $scopes)
	$name = $scope.name
	$description = $scope.description
Write-Output "Creating scope  $name"
Add-DhcpServerv4Scope -ComputerName $dhcpserver -Name "$name" -Description "$description" -StartRange $scope.startrange -EndRange $scope.endrange -SubnetMask $scope.subnetmask -State Active -LeaseDuration 1.00:00:00
Set-DhcpServerv4OptionValue -Router $scope.router -ScopeId $scope.scopeid -ComputerName $dhcpserver

Invoke webrequest example

This example uses invoke-webrequest to retrieve computer information from a company reporting webpage. Only text inside TD elements are stored in a array for future use and added to a PSObject.

function Get-ComputerInfo
			 ValueFromPipeline = $true,
			 position = 0,
			 Mandatory = $true)]

		$Request = @{
			'domain'	  = 'domain1.company.net'
			'name'    = $computername
		$lab = Invoke-WebRequest -Uri https://reporting.company.net/searchcomputer.php -Body $Request -Method Post

		$output = $lab.ParsedHtml.body.getElementsBytagname('TD') | select -expand innerhtml

		if ($output[7] -match '\D\d\d\d\d\d\d\d')
			$user = Get-ADUser -Identity $output[7] | select -ExpandProperty name
			$user = "Unknown"
			$output[7]= "Unknown"

		$result = New-Object PSObject -Property @{
			Computername	    = $output[0];
			OU				    = $output[2];
			IP				    = $output[6];
			OS				    = $output[3];
			LastUser		    = $output[7];
			LastUSerFullName    = $user;
			LastSeen		    = $output[5];
			Master			    = $output[4];
		Write-Output $result | select computername, ou, ip, os, master, lastuser, lastuserfullname, lastseen
	}#End Process

Migrate VMs between clusters with powecli

A short script to migrate VMs to a new cluster or host. Migrates one vm at a time to save network bandwith. After migration it upgrades Vmware tools to match current host version.

Takes a CSV file as input with VMs to migrate.

Param (
	[Parameter (Mandatory = $True)]
$vms = Import-Csv $file

foreach ($vm in $vms)
	Write-Host "Migrating VM" $vm.name
	Move-VM -VM $vm.name -Destination Cluster01
	Write-Host "Updating Vm-tools on " $vm.name
	Update-Tools -VM $vm.name -NoReboot

Fix VMware tools not running on Windows servers

I had some trouble with VMwaretools stopping on some machines and decided to automate restart och vmware tools. I wrote this script which takes a view parameter to view current status. If you start the script whitout the view parameter it will try to restart vmware tools.


[parameter(Mandatory = $false)]

#Connect to Vcenter
$cred = Get-Credential
Connect-VIServer vcenter -Credential $cred | Out-Null

Write-Host -ForegroundColor Green "Collecting VMs"
$toolsvm=get-vm | where { $_.GuestId -like "*Windows*" -and $_.Extensiondata.Summary.Guest.ToolsStatus -like "toolsnotrunning" -and $_.Powerstate -eq "PoweredOn"}

#Stop script if no VMs reports tools not running
if ($toolsvm -eq $null)
Write-Host "Nothing to fix"
Disconnect-VIServer vcenter -Confirm:$false

if ($view)
Write-Host "VMs whitout tools running"
foreach ($vm in $toolsvm)
Write-Host $vm

foreach ($vm in $toolsvm)

if ($vm -match "^(?<Name>\S*)\s.*$") { $vm_name = $Matches.Name }
else { $vm_name = $vm }

$vm_hostname = $vm_name.name +".domain.local"

if (Test-Connection -Computername $vm_hostname -BufferSize 16 -Count 1 -Quiet)
Invoke-Command -Computername $vm_hostname -ScriptBlock { stop-Service -name vmvss; start-service vmvss } -Credential $cred
Write-Host "Service restarted on $vm_name"
Write-Host -ForegroundColor Red "Unable to contact $vm_name"

#Disconnect from Vcenter.
Disconnect-VIServer vcenter -Confirm:$false

Create a new host via OP5 API with powershell

If you need to create a new host with OP5 api you can use something like this.
Template, hostgroups and contactgroups are not mandatory and can be removed from hash table.


function new-op5host
	[parameter(Mandatory = $true)]
	[parameter(Mandatory = $true)]
	$username = 'api_user$Default'
	$password = "password"
	$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $username, $password)))
	$hash = @{
		file_id = "etc/hosts.cfg";
		host_name = "$hostname";
		address = "$hostname.domain.com";
		alias = "$alias";
		max_check_attempts = "3";
		notification_interval = "5";
		notification_options = "d", "r";
		notification_period = "24x7";
		template = "Win_hosts";
		hostgroups = "Windows servers";
		contact_groups = "Windowsservrar"
	$jsonData = $hash | convertto-json
	$result = Invoke-RestMethod -Method POST -ContentType "application/json" -Uri "https://op5.domain.com/api/config/host" -Body $jsonData -Headers @{ Authorization = ("Basic {0}" -f $base64AuthInfo) }
	$result = Invoke-RestMethod -Method POST -ContentType "application/json" -Uri "https://op5.domain.com/api/config/change" -Headers @{ Authorization = ("Basic {0}" -f $base64AuthInfo) }