Backup and restore DHCP with Powershell

This is a helpful script for migrating from one DHCP server to another. With this, you can backup and restore DHCP with powershell. Run this script from a Domain controller or any other system that you are logged in as Domain Admin. When you run the script it will ask you the name of the source DHCP server. The leases and scopes from that server will be backed up locally on the system you run this from in a folder called DHCP. You will then be asked if you would like to restore. If you say yes, you will be asked what the destination server name is. It will now restore the scopes and leases you backed up earlier to the destination server. After this completes don’t forget to authorize the new server and de authorize the old one.

### Get source server name from user
$sourceServer = Read-Host "Enter the name of the source DHCP Server"
$choice = "n"

try {
    ###  Create DHCP Folder on local C:\ Drive
    New-Item -Name "DHCP" -ItemType Directory -Path C:\
    ###  Actual export command
    Export-DhcpServer -File "C:\dhcp\$sourceServer.xml" -Leases -Force -ComputerName $sourceServer –Verbose -ErrorAction Stop
    Write-Host "Export complete!"
    ###  Ask user to restore to a new server or not
    $choice = Read-Host "Would you like to restore to a new server? y or (n)"


    if ($choice -eq "y") {
        
        try {
            ###  Get Destination server from user
            $destServer = Read-Host "Enter the name of the destination DHCP Server"
            ###  Actual Import command
            Import-DhcpServer -File "C:\dhcp\$sourceServer.xml" -BackupPath C:\DHCP\ -Leases -ScopeOverwrite -Force -ComputerName $destServer –Verbose -ErrorAction Stop
            Write-Host "Success!  Make sure everything looks good!" -ForegroundColor Green
        }
        catch {
            Write-Host "Restore Failed.  Check your access to the new server and make sure DHCP is installed." -ForegroundColor DarkRed
            Exit
        }
    }


}
catch {
    Write-Host "Backup Failed.  Make sure you have access to the server and that you didnt typo the name" -ForegroundColor DarkRed
    Exit
}
Tagged : /

Add local ESXi users

For the most part you can use vCenter and powercli to get almost anything you need done for your virtual environment. However, sometimes you might run into a monitoring or management tool that does not have the ability to talk to vCenter. In this case we need to give the tool a local ESXi account, but we don’t to be handing out root password to automated systems. Adding a local user account can be done through the web ui for each ESXi host. If you have more than a few this could take a while. To solve this we can use our good friend powershell. To do add these users we will need to connect to each host using ssh. I tried just using the built-in ssh that windows/powershell can do but it just didn’t work right. To get over this I used a tool called kitty this is a modified putty that lets you run it from command line. You can use plink as well but I already had kitty available and some code I had written for a different task. Another thing you need before starting is vmware powercli. You can see this post on how to set that up. The code below will connect to vCenter, look for the host(s) we define, then for each of the hosts it will enable ssh via powercli, connect via kitty (ssh) and run our commands to create the user and set read-only permission, finally it will disable ssh.

#Check for Creds and ask for them if they aren't found
if (!($Creds)) {$Creds = get-credential -Message "Enter your vCenter Admin Creds in domain\username format"}

# Connect to vCenter
Connect-VIServer -server yourvCenterIPorName -Credential $creds -force

#get all hosts.  You can use an array here instead, but this will get all of the hosts connected to your vCenter
$esxHosts = Get-VMHost 


foreach ($esxHost in $esxHosts) {
    
    $esxHost = $esxHost.name
    #Start SSH Service
    Get-VMHostService -VMHost $esxHost | Where-Object {$_.Key -eq "TSM-SSH" } | Start-VMHostService

    #run commands to create user and set read-only permissions
.\kitty.exe -kload .\kex.ktx root@$esxHost -pass "ESXiPassword" -cmd '
esxcli system account add -d=''read-only local user'' -i=''username'' -p=''Password'' -c=''Password''
esxcli system permission set -i=''username'' -r=''ReadOnly''
esxcli system permission list
exit
'

    
    #Stop SSH Service
    Get-VMHostService -VMHost $esxHost | Where-Object {$_.Key -eq "TSM-SSH" } | Stop-VMHostService -Confirm:$false


}

Remove a local user

If you need to remove the user we can modify the above script a bit to remove the user from all the hosts. That looks like this:

#Check for Creds and ask for them if they aren't found
if (!($Creds)) {$Creds = get-credential -Message "Enter your vCenter Admin Creds in domain\username format"}

# Connect to vCenter
Connect-VIServer -server yourvCenterIPorName -Credential $creds -force

#get all hosts.  You can use an array here instead, but this will get all of the hosts connected to your vCenter
$esxHosts = Get-VMHost 


foreach ($esxHost in $esxHosts) {
    
    $esxHost = $esxHost.name
    #Start SSH Service
    Get-VMHostService -VMHost $esxHost | Where-Object {$_.Key -eq "TSM-SSH" } | Start-VMHostService

#run commands to remove the user
.\kitty.exe -kload .\kex.ktx root@$esxHost -pass "ESXiPassword" -cmd '
 esxcli system account remove -i=''username''
 exit
 '

   
    #Stop SSH Service
    Get-VMHostService -VMHost $esxHost | Where-Object {$_.Key -eq "TSM-SSH" } | Stop-VMHostService -Confirm:$false


}
Tagged : /

Run Powershell commands on many systems

One of the best things about powershell is the ability to run commands on many systems at once or in a loop. There are a few ways to do this and I’ll go over a few of my favorite in this post. First, We need to get an array filled with the systems that we want to work on.

Fill Array with Systems

This will be used in each of our methods for running commands on systems. Here is how the code looks:

# initialize the array
$systems = @()

$systems = (
     "System1",
     "System2",
     "System3"
)

Multiple PSSessions

First up is the simplest we will use New-PSSession and Invoke-Command. Since we already have our array of systems here is how you create the PSSession to ALL of the systems at once:

#Check for Creds and ask for them if they aren't found
if (!($Creds)) {$creds = get-credential -Message "Enter your Admin Creds"}

# Create PSSessions to all the systems in the array
$sessions = New-PSSession -ComputerName $systems -Credential $Creds

Now that we have our sessions we can run whatever commands we want using invoke-command. Here is an example of running gpupdate on all the systems at once. Whatever you put inside the curly brackets is what will be run on each system. For ease knowing the the heck happened I always put the hostname command first. This just prints the hostname right before the results of the second commands so you can tell if the command worked on each system. You can string multiple commands together with the semi-colon (;).

Invoke-Command -Session $sessions -ScriptBlock {hostname;gpupdate /force /boot}

Foreach loop

Depending on the command you are running maybe you don’t want all the systems to run the command at the same time. You can accomplish this with a foreach loop. We will still use invoke-command, but the commands will run one system at a time. We will run the same gpupdate command. Here is how that code looks:

foreach ($system in $systems){
     Invoke-Command -Computername $system -ScriptBlock {hostname; gpupdate /force /boot}
}

Foreach-Object loop (PS 7+ only)

Starting in Powershell 7 we have a new way to run for loops. We can run them in a mode called parallel. Instead of the old school for loop that runs one iteration at a time, it will run multiple iterations at once. You can even throttle it to say run 2 or three at once. This is my favorite loop type for sure. I mention the throttle because depending on the command you are trying to run if you have too many threads running at once its actually slower than a traditional for loop. Crazy right? You will see it as you use it more, but for the most part you can let it rip and its waaay faster than waiting on a regular loop. Here is how it looks with the same command we have been using:

$systems | ForEach-Object -Parallel  {
     Invoke-Command -Computername $_ -ScriptBlock {hostname; gpupdate /force /boot}
}

Notice that we use the $_ for the computer name. This is how you access the array one item at a time in this type of loop.

Tagged :

Read text from a PDF with Powershell

The other day I helped a co worker with a script he was working on. He needed to read text from a PDF with Powershell. I had done this in the past with autoit but that wasn’t going to be an option this time. There are a lot of posts about this online but they almost all lead to itext7 I don’t if my co worker and I are just dumb but we just could not get their module installed. I did end up finding a different way to get this done. You really just need this DLL that has the library to deal with PDF files. I cant upload it here but you can get it easily.

Get DLL

You can download the .DLL file from this site (UPDATE 10-19-21: the original .dll is no longer at the original link. Another commenter pointed out its on github here also I created a share link for the .dll I use for this and I know this one works. That link is here) . When you get to the site click the “Download Archive” button. This will give you a zip file. Extract it, inside the folder open sourceCode, Main, Libraries. There you will find itextsharp.dll. Copy this file to C:\PS\ (this is where our script will look).

Read text from PDF file

I made this into a function so it is easy to use in a larger script. here it is:

function convert-PDFtoText {
	param(
		[Parameter(Mandatory=$true)][string]$file
	)	
	Add-Type -Path "C:\ps\itextsharp.dll"
	$pdf = New-Object iTextSharp.text.pdf.pdfreader -ArgumentList $file
	for ($page = 1; $page -le $pdf.NumberOfPages; $page++){
		$text=[iTextSharp.text.pdf.parser.PdfTextExtractor]::GetTextFromPage($pdf,$page)
		Write-Output $text
	}	
	$pdf.Close()
}

This is is an example of how to run it and display the results to the screen.

$file = "C:\Path\To\PDF.pdf"

convert-PDFtoText $file

With this example we set the text into a variable for later use.

$file = "C:\Path\To\PDF.pdf"
$text = convert-PDFtoText $file
Tagged :

Create Azure vnet, subnet and Network security group with application policies with powershell

In a previous post I showed you how to create a resource group. The next thing I want to show you is how to create a vnet, subnet and a network security group with application security rules built in. This will result in a new network where we can place VMs or other objects. I am going to break down each section of this script and explain what is happening. At the end, I will put it all together so you can tweak it and use in your own environment.

Variables

This is the section you will change to fit your resource group, location and subnet preferences.

### Change these to your liking
$LocationName = "westus"
$ResourceGroupName = "Skylar-Testing"
$VnetAddressPrefix = "10.50.0.0/24"
$SubnetAddressPrefix = "10.50.0.0/24"

### Change these only if you don't like my naming convention
$NetworkName = "$ResourceGroupName-vnet"
$SubnetName = "$ResourceGroupName-subnet"

Create Application Security Groups

Application security groups are what you will use to define allow/deny rules based on ports for your VMs. In my example I make 3 groups;

Management-Linux
– I will use this group to attach a rule to allow SSH (Port 22) traffic.

Management-Windows
– I will use this group to attach a rule to allow RDP (Port 3389) traffic.

Web
– I will use this group to attach a rule to allow Web (Ports 80, 443) traffic.

### Create Application security Groups if they dont already exist
try {
    $asgLinux = get-AzApplicationSecurityGroup -Name "Management-Linux" -ResourceGroupName $ResourceGroupName -ErrorAction Stop
}
catch {
    $asgLinux = New-AzApplicationSecurityGroup -Name "Management-Linux" -Location $LocationName -ResourceGroupName $ResourceGroupName
}
try {
    $asgWindows = get-AzApplicationSecurityGroup -Name "Management-Windows" -ResourceGroupName $ResourceGroupName -ErrorAction Stop
}
catch {
    $asgWindows = New-AzApplicationSecurityGroup -Name "Management-Windows" -Location $LocationName -ResourceGroupName $ResourceGroupName  
}
try {
    $asgWeb = get-AzApplicationSecurityGroup -Name "Web" -ResourceGroupName $ResourceGroupName -ErrorAction Stop
}
catch {
    $asgWeb = New-AzApplicationSecurityGroup -Name "Web" -Location $LocationName -ResourceGroupName $ResourceGroupName
}

These are just the groups I am making, your needs may vary and in the next section you will see where the actual rules come into play.

Create Network Security Group (NSG) and apply rules

Here is where we create the actual nsg and apply rules to it. Your rules may vary but I think this is a good start. You might notice that the priority of each rule is set 10 apart. This allows us to add rules in between these few defaults. That way we can override one of these with a rule for a specific situation and put the priority lower than the more strict default rule. Another thing to note on the two management rules. In the code for this example I have the SourceAddresssPrefix set to Internet. This is not best practice for an actual production environment. Ideally you just use this for testing or better yet scope it down to your public IP. It would look something like 0.0.0.0/32 where the 0’s would be your public IP. You can get that easily here.

### Check to see if the NSG already exists and creates if not
try {
    $nsg = get-AzNetworkSecurityGroup -ResourceGroupName $ResourceGroupName -Name "$NetworkName-NSG" -ErrorAction Stop
}
catch {
    ### Security Group rules
    $RDP_Rule = New-AzNetworkSecurityRuleConfig -Name rdp-rule -Description "Allow RDP" `
    -Access Allow -Protocol Tcp -Direction Inbound -Priority 100 `
    -SourceAddressPrefix Internet -SourcePortRange * `
    -DestinationApplicationSecurityGroup $asgWindows -DestinationPortRange 3389

    $HTTP_Rule = New-AzNetworkSecurityRuleConfig -Name http-rule -Description "Allow HTTP" `
    -Access Allow -Protocol Tcp -Direction Inbound -Priority 110 `
    -SourceAddressPrefix Internet -SourcePortRange * `
    -DestinationApplicationSecurityGroup $asgWeb -DestinationPortRange 80

    $HTTPS_Rule = New-AzNetworkSecurityRuleConfig -Name https-rule -Description "Allow HTTPS" `
    -Access Allow -Protocol Tcp -Direction Inbound -Priority 120 `
    -SourceAddressPrefix Internet -SourcePortRange * `
    -DestinationApplicationSecurityGroup $asgWeb -DestinationPortRange 443

    $SSH_Rule = New-AzNetworkSecurityRuleConfig -Name ssh-rule -Description "Allow SSH" `
    -Access Allow -Protocol Tcp -Direction Inbound -Priority 130 `
    -SourceAddressPrefix Internet -SourcePortRange * `
    -DestinationApplicationSecurityGroup $asgLinux -DestinationPortRange 22 

    ### Create Network Security Group
    $nsg = New-AzNetworkSecurityGroup -ResourceGroupName $ResourceGroupName -Location $LocationName -Name "$NetworkName-NSG" -SecurityRules $RDP_Rule,$SSH_Rule,$HTTP_Rule,$HTTPS_Rule

}

Create vnet and subnet

Now that we have our asg’s and nsg we can create the vnet and associate it with what we created above. This is probably the shortest most straight forward section, but it couldn’t exist without the stuff we just did. Well it could, just wouldn’t have any security and we don’t want that! This section will check and make sure the vnet doesn’t already exist and then it uses all the info from above to create the vnet and the subnet inside. You shouldn’t have to edit this section.

#Check for Network and create if it does not exist
try {
    Get-AzResourceGroup $ResourceGroupName | Get-AzVirtualnetwork -Name $NetworkName -ErrorAction Stop
    Write-Host "Network exists!" -ForegroundColor DarkGreen
}
catch {
    Write-Host "Network does not exist.  It will now be created." -ForegroundColor DarkRed
    ### Create vnet and subnet
    $SingleSubnet = New-AzVirtualNetworkSubnetConfig -Name $SubnetName -AddressPrefix $SubnetAddressPrefix -NetworkSecurityGroup $nsg -ErrorAction Stop
    $Vnet = New-AzVirtualNetwork -Name $NetworkName -ResourceGroupName $ResourceGroupName -Location $LocationName -AddressPrefix $VnetAddressPrefix -Subnet $SingleSubnet -ErrorAction Stop
    Write-Host "Completed!" -ForegroundColor DarkGreen    
}

Put it all together

Here is the script in its entirety. Hopefully the explanation helped. This was kind of weird the first few times I did it.

$LocationName = "westus"
$ResourceGroupName = "Testing42"
$NetworkName = "$ResourceGroupName-vnet"
$VnetAddressPrefix = "10.50.0.0/24"
$SubnetName = "$ResourceGroupName-subnet"
$SubnetAddressPrefix = "10.50.0.0/24"


### Create Application security Groups if they dont already exist
try {
    $asgLinux = get-AzApplicationSecurityGroup -Name "Management-Linux" -ResourceGroupName $ResourceGroupName -ErrorAction Stop
}
catch {
    $asgLinux = New-AzApplicationSecurityGroup -Name "Management-Linux" -Location $LocationName -ResourceGroupName $ResourceGroupName
}
try {
    $asgWindows = get-AzApplicationSecurityGroup -Name "Management-Windows" -ResourceGroupName $ResourceGroupName -ErrorAction Stop
}
catch {
    $asgWindows = New-AzApplicationSecurityGroup -Name "Management-Windows" -Location $LocationName -ResourceGroupName $ResourceGroupName  
}
try {
    $asgWeb = get-AzApplicationSecurityGroup -Name "Web" -ResourceGroupName $ResourceGroupName -ErrorAction Stop
}
catch {
    $asgWeb = New-AzApplicationSecurityGroup -Name "Web" -Location $LocationName -ResourceGroupName $ResourceGroupName
}


### Check to see if the NSG already exists and creates if not
try {
    $nsg = get-AzNetworkSecurityGroup -ResourceGroupName $ResourceGroupName -Name "$NetworkName-NSG" -ErrorAction Stop
}
catch {
    ### Security Group rules
    $RDP_Rule = New-AzNetworkSecurityRuleConfig -Name rdp-rule -Description "Allow RDP" `
    -Access Allow -Protocol Tcp -Direction Inbound -Priority 100 `
    -SourceAddressPrefix Internet -SourcePortRange * `
    -DestinationApplicationSecurityGroup $asgWindows -DestinationPortRange 3389

    $HTTP_Rule = New-AzNetworkSecurityRuleConfig -Name http-rule -Description "Allow HTTP" `
    -Access Allow -Protocol Tcp -Direction Inbound -Priority 110 `
    -SourceAddressPrefix Internet -SourcePortRange * `
    -DestinationApplicationSecurityGroup $asgWeb -DestinationPortRange 80

    $HTTPS_Rule = New-AzNetworkSecurityRuleConfig -Name https-rule -Description "Allow HTTPS" `
    -Access Allow -Protocol Tcp -Direction Inbound -Priority 120 `
    -SourceAddressPrefix Internet -SourcePortRange * `
    -DestinationApplicationSecurityGroup $asgWeb -DestinationPortRange 443

    $SSH_Rule = New-AzNetworkSecurityRuleConfig -Name ssh-rule -Description "Allow SSH" `
    -Access Allow -Protocol Tcp -Direction Inbound -Priority 130 `
    -SourceAddressPrefix Internet -SourcePortRange * `
    -DestinationApplicationSecurityGroup $asgLinux -DestinationPortRange 22 

    ### Create Network Security Group
    $nsg = New-AzNetworkSecurityGroup -ResourceGroupName $ResourceGroupName -Location $LocationName -Name "$NetworkName-NSG" -SecurityRules $RDP_Rule,$SSH_Rule,$HTTP_Rule,$HTTPS_Rule

}

#Check for Network and create if it does not exist
try {
    Get-AzResourceGroup $ResourceGroupName | Get-AzVirtualnetwork -Name $NetworkName -ErrorAction Stop
    Write-Host "Network exists!" -ForegroundColor DarkGreen
}
catch {
    Write-Host "Network does not exist.  It will now be created." -ForegroundColor DarkRed
    ### Create vnet and subnet
    $SingleSubnet = New-AzVirtualNetworkSubnetConfig -Name $SubnetName -AddressPrefix $SubnetAddressPrefix -NetworkSecurityGroup $nsg -ErrorAction Stop
    $Vnet = New-AzVirtualNetwork -Name $NetworkName -ResourceGroupName $ResourceGroupName -Location $LocationName -AddressPrefix $VnetAddressPrefix -Subnet $SingleSubnet -ErrorAction Stop
    Write-Host "Completed!" -ForegroundColor DarkGreen    
}
Tagged : / / /

Use Powershell profiles

Powershell profiles are like startup programs for powershell. You can have it load in creds, set variables, login to a system, set formatting, and much more. I use a few simple tweaks to my profile that I think are helpful.

Profile location

First we need to know where the profile script is located. If you are using regular powershell or VSCode the path may be different so keep that in mind. To file your profile simple run the $PROFILE variable:

$PROFILE
Powershell profile path

Edit your profile

Now simply open the script and edit it. Anything you want to happen each time you open a powershell window can go in here. Here are a few that I like to use.

Timestamp on each new line

example of timestamp

I like to have a timestamp on each new line in my powershell window. I can easily see how long a script has run or what time I did a certain task. Add this code into your profile file to enable this:

function prompt {
    "[$(Get-Date -format 'hh:mm:ss tt')] PS $($executionContext.SessionState.Path.CurrentLocation)$('>' * ($nestedPromptLevel + 1)) ";
    }

Set path variable

I set a path variable for the base of my powershell scripts. There are other ways to do this but I like my variable. You can do this just as you would think, just set a variable:

$HD = "C:\Path\to\powershell\scripts"

This makes it so all I need to do to get back to my home directory and type in $HD.

Tagged : /

Create an Azure Resource Group with Powershell

Resource groups are a fundamental component of Azure. Your VMs, Networks, Storage account(s), etc. all live in resource groups. Before we run the command we need to figure out WHERE we want to put the resource group. You can do this a few ways; one way is to pick from Microsoft’s website here. Another way is to list them from powershell:

List locations:

#List all Locations
Get-AzLocation

#To filter the list
Get-AzLocation | where {$_.Displayname -like "*west*"}

The info we need to is the Location. Once we have our location, we are ready to create our new resource group. You can do so with the following command. Make sure you are connected to Azure, you can see how to connect here.

Create Resource Group (Single Command):

New-AzResourceGroup -Name "Testing42" -Location "westus"

Create Resource Group (With Error Checking):

This is the same thing as above, just with a try-catch statement for error checking. This is what you would want to use in a larger script to make sure the resource group already existed.

$LocationName = "westus"
$ResourceGroupName = "Testing42"

#Check for resource group and create if it does not exist
try {
    Get-AzResourceGroup -Name $ResourceGroupName -ErrorAction Stop
    Write-Host "Resource group exists.  VM will be placed along side existing resources" -ForegroundColor DarkGreen
}
catch {
    Write-Host "Resource group does not exist.  It will now be created." -ForegroundColor DarkRed
    try {
        New-AzResourceGroup -Name $ResourceGroupName -Location $LocationName -Verbose -ErrorAction Stop        
        Write-Host "Completed!" -ForegroundColor DarkGreen        
    }
    catch {
        Write-Host "Failed to create resource group"
        exit        
    }
}
Creating a resource group

After running the command we get output that it was successful. It takes a minute or two for the resource group to show up in the Azure portal. If we want remove the resource group that can be done with this command.

Remove Resource Group:

Get-AzResourceGroup -Name "Testing42" | Remove-AzResourceGroup
Removing a resource group
Tagged : /

Chocolatey is awesome

If you have not heard of chocolatey, you are in luck! I will introduce you. Chocolatey is a package management tool for Windows, its a lot like apt or yum if you have used linux. With Chocolatey you can leverage powershell scripts to deploy and update apps on your local PC or remote servers or workstations. There site is here and goes into a lot more detail of how it all works: https://chocolatey.org/ In this post I will show you a few ways that I use Chocolatey to make my life easier.

First, the basics, Lets install it:

Install Chocolatey

This script will check to see if Chocolatey is installed by simply running the “choco” command to see if it returns anything. If it does not it will then attempt to install Chocolatey from their website.

try {
	invoke-command -scriptblock {choco} -erroraction stop
	write-host "Has Choco. all is good!"
}

catch {
	Write-Host "Needs Choco.  Trying install..." 
	try {
		invoke-command -scriptblock {Set-ExecutionPolicy Bypass -Scope Process -Force; Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))} -ErrorAction Stop
	}
	catch {
		write-host "Install Failed"
	}
		
}

But how do you search for apps!?

Find Choco apps

There are a couple ways to do this.

First, you can go to the website and look through the gallery here: https://chocolatey.org/packages

Second, you can use command line to search for a specific app:

choco search appname
or
choco list appname

Lastly you can install the chocolateygui app and use it to search. You can install it with this:

install chocolateygui -y

Once installed you will find it in your start menu. Simply open it and there is a nice search to look for new apps and update what you already have installed.

Ok, found and app. Lets install it.

Install Choco apps

Installing choco apps is just as easy as the searching. You can install one app at a time or multiple. You can always use the ChocolateyGUI app from the previous section but I want to show you the command line ways. Here are a couple ways to do it:

One at a time:

choco install packagename -y

Notice the -y at the end. If you don’t add that you will need to accept the install. Another switch that can be added is -f. If you add that switch it will force install the app even if its installed. Meaning it will reinstall what is already installed. I have mixed luck with that so keep it in mind that it depends on the app if you can use the -f switch.

Multiple apps at a time in one line:

choco install package1 package2 package3 -y

This method works fine but I have found that if you have more than a few apps the command gets a little long. I have another method for installing multiple apps using an array to hold the package names instead. It just looks better when you have a larger list of apps and is easier the change out apps as needed. Here is the one is use:

if (!([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) { Start-Process powershell.exe "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`"" -Verb RunAs; exit }

#List packages you want to install
$packages = (
    "chocolateygui",
    "googlechrome",
    "notepadplusplus",
    "vlc",
    "winscp",
    "nmap",
    "autoit",
    "7zip",
    "putty",
    "git",
    "vscode",
    "openvpn",
    "vim",
    "mobaxterm",
    "adobereader",
    "rsat",
    "wireshark",
    "mousewithoutborders",
    "forticlientvpn",
    "microsoft-teams",
    "Office365Business"
)

#Loop through the packages
foreach ($package in $packages){

   #Instal each package and force a reinstall if its already there
   choco install $package -y -f


}

As you can see you can pretty much get all of your normal apps installed in one script. That top line is something I add to a lot a scripts that I plan on running from the actual .ps1 file. The line will check to see if the powershell session is running as admin and will reopen it as admin if not.

Final thing, updating!

Update Choco apps

There are a few ways to do this as well. Again you can use the ChocolateyGUI app to update one or all of the apps but here are the powershell ways:

Update one app:

choco upgrade packagename -y

Update all apps:

choco upgrade all -y

This will upgrade all the apps that you have installed via chocolatey. One of my favorite commands!

Tagged : / /

Use Powershell to Create and Add members to Active Directory Groups from csv

This is a task that I feel like there are a ton of ways to do it, this is just mine. With this you create a csv file with two columns; the first is the username of the person you want to add to the group and the second is the group that the person should be in. The script will check to see if the group exists and if it doesn’t it will be created in the path you define and then it will add the member, if the group does already exists it simply adds the user. This is a fast way to create a bunch of groups if you need to. For connecting to Active directory see this post.

Script:

#You need to have a connection to AD first or run this from a DC (see http://allthesystems.com/2020/08/powershell-connection-examples/)
#Define path to csv and basepath to create groups if they dont exist
$list = Import-Csv -Path C:\Path\to\csv\newgroups.csv
$BasePathForGroups = "OU=Path,OU=To,OU=OU,DC=Domain,DC=com"

foreach ($item in $list) {
    #Set variables for loop from csv line
    $group = $item.group
    $member = $item.member

    #Check if group exists
    if(Get-ADGroup $group){

        Write-Host "group exists. adding member: $member"
        #Adds member
        Add-ADGroupMember -Identity $group -Members $member

    }
    else {
        Write-Host "Group doesnt exist.  creating: $group"
        #Creates group
        New-ADGroup -Name $group -SamAccountName $group -GroupCategory Security -GroupScope Global -DisplayName $group -Path $BasePathForGroups
        #Adds member
        Add-ADGroupMember -Identity $group -Members $member 
    }

}
Tagged : / /

Powershell Add Portgroups to VMWare Hosts

If you don’t have licensing for distributed switches in vCenter adding portgroups to a new host or a new cluster can be super time consuming in the UI BUT thankfully VMWare has had mercy on us and provided comandlets to use Powershell to add portgroups to VMWare hosts. This is a script I use a lot to add portgroups to hosts and it is way faster and way less painful than doing it by hand so… enjoy! For PowerCLI setup and connecting to vCenter see this post.


Script:

#Check for Creds and ask for them if they aren't found
if (!($Creds)) {$creds = get-credential -Message "Enter your vCenter Creds"}
#Change these to your values
$vSwitchName = "vSwitch_Name"
$PortgroupName = "Name_of_Portgroup"
$VLanId = "123"
$vCenter_Server = "vCenter_Server_Name"
$Hosts = (
    "Host1",
    "Host2",
    "host3"
)

#Connect to vCenter
Connect-VIServer -server $vCenter_Server -Credential $creds -force

foreach ($Host in $Hosts) {
    #Adds portgroup to host
    get-vmhost -Name $Host | get-virtualswitch -name $vSwitchName |  New-VirtualPortGroup -name $PortgroupName -VLanId $VLanId
}
Tagged : / / /

Powershell Diagram vCenter

This is a script that I got from someone else but have modified pretty heavily. This will use Powershell to diagram vCenter. This will connect to vCenter, pull a list of clusters and for each cluster it will create a Visio diagram and detail host, datastore and VM info. For this to run you need to have Visio installed. You also need these templates which apparently I’m not allowed to link to but you can find them pretty easy. The two non-standard ones I use are “EUC Visio Stencils 2018” and “Security and Systems Stencil.” You don’t NEED to use mine just make sure you update the section of the script that ties a stencil to a type of system. (its commented in there starting at line 69). The last thing needed is the PowerCLI module installed. You can see how that works here.

Script:

#Check for Creds and ask for them if they aren't found
if (!($Creds)) {$creds = get-credential -Message "Enter your vCenter Creds"}

$vCenter_Server = "vCenter_Server_Name"
Connect-VIServer -server $vCenter_Server -Credential $creds -force
$allclusters = Get-Cluster

foreach ($cluster in $allclusters)
{
$shpFile1 = "EUC Visio Stencils 2018.vss"
$shpFile2 = "Security and Systems Stencil.vssx"
$shpFile3 = "Basic Shapes.vss"
 
#VISIO Function for the Connection-of-objects  
function connect-visioobject ($firstObj, $secondObj)  
{  
    $shpConn = $pagObj.Drop($pagObj.Application.ConnectorToolDataObject, 0, 0)  
    #// Connect its Begin to the 'From' shape:  
    $connectBegin = $shpConn.CellsU("BeginX").GlueTo($firstObj.CellsU("PinX"))  
    #// Connect its End to the 'To' shape:  
    $connectEnd = $shpConn.CellsU("EndX").GlueTo($secondObj.CellsU("PinX"))  
}  
 
#VISIO Function for adding the object into the drawing  
function add-visioobject ($mastObj, $item)  
{  
         Write-Host "Adding $item"  
        # Drop the selected stencil on the active page, with the coordinates x, y  
          $shpObj = $pagObj.Drop($mastObj, $x, $y)  
        # Enter text for the object  
          $shpObj.Text = $item
         
        #Resize the object
          $shpObj.Resize(1,150,33)
          
        
        #Return the visioobject to be used  
        return $shpObj  
 }  
 
# Create VI Properties to extract vmtype
 
New-VIProperty -Name GuestFamily -ObjectType VirtualMachine -ValueFromExtensionProperty 'guest.guestfamily' -Force | Out-Null
New-VIProperty -Name GuestOSType -ObjectType VirtualMachine -ValueFromExtensionProperty 'guest.guestfullname' -Force | Out-Null
 
 
# Create an instance of Visio and create a document based on the Basic Diagram template.  
$AppVisio = New-Object -ComObject Visio.Application -ErrorAction Stop
$AppVisio.Visible = $false
$docsObj = $AppVisio.Documents  
$DocObj = $docsObj.Add("Basic Network Diagram.vst")  
 
# Set the active page of the document to page 1  
$pagsObj = $AppVisio.ActiveDocument.Pages  
$pagObj = $pagsObj.Item(1)  
 
# Load a set of stencils and select one to drop  
$stnPath = [system.Environment]::GetFolderPath('MyDocuments') + "\My Shapes\"  
$stnObj1 = $AppVisio.Documents.Add($stnPath + $shpFile1)  
$stnObj2 = $AppVisio.Documents.Add($stnPath + $shpFile2)
$stnObj3 = $AppVisio.Documents.Add($shpFile3)

$VirtualMachine = $stnobj1.Masters.item("Virtual Machine (3D)") 
$Windows = $stnobj1.Masters.item("Microsoft")
$PoweredOff =  $stnobj2.Masters.item("VM State Saved")
$Linux =  $stnobj2.Masters.item("Linux")
$VirtualAppliance = $stnobj1.Masters.item("3D Virtual Appliance")  
$vSphere = $stnobj1.Masters.item("vCenter")
$Clusters = $stnobj1.Masters.item("Cluster")
$VMware_Host=$stnobj1.Masters.item("ESXi Host")
$datastores=$stnobj1.Masters.item("Storage 1")
$vSwitchObject=$stnobj1.Masters.item("vSwtch 3D")
$Network = $stnobj2.Masters.item("Process")
$Switch = $stnobj1.Masters.item("Switch")
$Firewall = $stnobj1.Masters.item("Router 1")
$Rectangle = $stnObj3.Masters.item("Rounded Rectangle")


 
$allNODES = Get-Cluster $cluster | get-vmhost
$allclusters = Get-Cluster $cluster
$allVMs = Get-Cluster $cluster | Get-VM  
$allDs = Get-Cluster $cluster | Get-Datastore

 
 
#Set Start Locations of Objects  
$x = 1  
$y = .5  
 
#DRAW ALL Cluster-NODES  
Foreach ($cluster in $allclusters) {  
    $y += 0.25  
    $VMs_Total = get-cluster $cluster | get-vm | Measure-Object | Select-Object -ExpandProperty count
    $clusterInfo = "Cluster: " + $cluster.Name + "`nTotal VMs: " + $VMs_Total
    $clusterObj = add-visioobject $Clusters $clusterInfo
  
#Find Datastores and draw them
    Foreach ($d in $allDs) {  
          
            #calculate percent free            
            $percentFree = [math]::Round(($d.FreeSpaceGB / $d.CapacityGB * 100),0)
            $x = -3             
            $dsInfo = "Datastore: " + $d.Name  + "`nCapacity: " + [math]::Round([decimal]$d.CapacityGB,2) + "GB`nFree Space: " + [math]::Round([decimal]$percentFree,2) + "%"
            $datastoreObj = add-visioobject $datastores $dsInfo  
            connect-visioobject $clusterObj $datastoreObj
            $y += 4     
    }

    $allvSwitches = Get-VMHost $allNODES[0] | Get-VirtualSwitch
        $x +=4
    #DRAW ALL Virtual Switches and connect them to the cluster object
        Foreach ($vSwitch in $allvSwitches) {  
                
            $Portgroups = Get-VirtualPortGroup -VirtualSwitch $vSwitch | Select-Object name, vlanid
            $portgroupnames = ""
            foreach ($pg in $Portgroups){
                $name = $pg.name
                $vlan = $pg.vlanid
                $portgroupnames = $portgroupnames + "$name($vlan)`n" 
            
            }
            $x += 5  
            $y = -1
            #$vmCount = Get-VMHost $allNODES[0] | Get-VirtualPortGroup | get-vm | Measure-Object | Select-Object -ExpandProperty count  
            $vsInfo = "vSwitch: " + $vSwitch.Name  + "`nPortgroupname(Vlan): " + $portgroupnames #+ "`nVM Count: " + $vmCount
            $vSwitches = add-visioobject $vSwitchObject $vsInfo  
            connect-visioobject $ClusterObj $vSwitches

        }
    
            $y +=15
#DRAW ALL Physical VMHOST NODES with Node Name, Total Memory and vCenter version and connect them to the cluster object
    Foreach ($node in $allNODES) {  
          
            $x = 5  
            
            $Mem_Percent = [math]::Round(($node.MemoryUsageGB / $node.MemoryTotalGB * 100),0)
            $VMs_Total = Get-vmHost $node | get-vm | Measure-Object | Select-Object -ExpandProperty count  
            $nodeInfo = "Host: " + $node.Name  + "`neSXIVersion: " + $node.Version + "`nTotalMemory: " + [math]::Round([decimal]$node.MemoryTotalGB,2) + "GB`nUsed Memory: " + [math]::Round([decimal]$Mem_Percent,2) + "%" + "`nTotal VMs: " + $VMs_Total
            $nodeObj = add-visioobject $VMware_Host $nodeInfo  
            connect-visioobject $clusterObj $nodeObj      
            


# GET All Virtual Machines and drwa them based on the OS type and connect them to the Node object.
$allVMNodes = Get-VMHost $node | Get-VM | select guestfamily,name,NumCpu,MemoryGB,guestostype,notes,PowerState 
                $y -= 1.5
        foreach ($vm in $allVMNodes) {   
        
            $x += 5          
            #$y += 1.5
            $guestinfo = Get-VMGuest $vm.name | Select-Object IPAddress,Disks, Nics
            $ipinfo = ""
            foreach($nic in $guestinfo.Nics){
                $ipinfo += $nic.device.networkname + " - " + $nic.IPAddress + "`n"

            }

            $diskinfo = ""
            foreach($disk in $guestinfo.Disks){
                $diskinfo += $disk.Path + "`nCapacity: " + [math]::Round($disk.CapacityGB,2)  + "GB`nFree Space: " + [math]::Round($disk.FreeSpaceGB,2) + "GB`n"
            }
            $vmInfo = "VM Name: " + $vm.Name + "`n`nVM OS: " + $vm.guestostype + "`n`nCPU count: " + $vm.NumCPU + "`nRAM: " + $vm.MemoryGB + "GB`n`nIP: " + $ipinfo + "`nDisk Info: `n" + $diskinfo + "`n`nNotes: " + $vm.notes  
            if ($vm.GuestOSType -like "*Windows*") {
                $VirtualMachineObj = add-visioobject $Windows $vmInfo
            }
            elseif ($vm.GuestOSType -like "*Linux*") {
                $VirtualMachineObj = add-visioobject $Linux $vmInfo
            }
            elseif ($vm.GuestOSType -like "*CentOS*") {
                $VirtualMachineObj = add-visioobject $Linux $vmInfo
            }
            elseif ($vm.PowerState -eq "PoweredOff") {
                $VirtualMachineObj = add-visioobject $PoweredOff $vmInfo
            } 
            else {
                $VirtualMachineObj = add-visioobject $VirtualMachine $vmInfo
            }
           
            connect-visioobject $nodeObj $VirtualMachineObj  
        
        }  
        $y += 15
        }
 
        }
        
        $y -= 12
        $x = -2
     



# Resize the Visio so that all fits in one page, nice and clean.
$pagObj.AutoSizeDrawing()
$pagObj.ResizeToFitContents()  

# Save the Visio file generated in desktop for each of the cluster.
$DocObj.SaveAs("C:\$cluster-Detailed.vsd")
$pagObj.Export("C:\$cluster-Detailed.png")
$AppVisio.quit()
Write-Host "Finished $cluster"
        
}
#Stop-Process -Name "*Visio*"
Write-Host "Complete!"
Tagged : / /

Powershell Install SCCM Client

There have been times where I have run into issues where the SCCM client doesn’t install on a new server or I am trying to finish a server setup quickly and I don’t want to wait for SCCM to do it automatically. Here is a little snip that will let you put a list of server or workstions into an array and it will copy the client locally and then run user powershell to install SCCM client.

Script:

#Check for Creds and ask for them if they aren't found
if (!($Creds)) {$creds = get-credential -Message "Enter your Domain Admin Creds"}

#Change this path, this should be pretty close to yours
$ClientPath = "\\SCCM_Server_Name\SMS_SITE\Client\ccmsetup.exe"

#List of Servers goes here
$servers = (
    "Server1",
    "Server2",
    "Server3" 
)

#This will clear any PSSessions
Remove-PSSession *

#Creates a PSSession for each server defined above and copies the most current client .exe locally
foreach ($server in $servers) {
    $s = New-PSSession -ComputerName $server -Credential $Creds
    Copy-Item $ClientPath -Destination "C:\ccmsetup.exe" -ToSession $s -Force
    Remove-PSSession $s
}

#Runs the client installer
$s = New-PSSession -ComputerName $servers -Credential $Creds
Invoke-Command -Session $s -ScriptBlock {
    cd C:\ ;
    .\ccmsetup.exe /mp:SCCM_Server_Name /logon SMSSITECODE=AUTO FSP=SCCM_Server_Name;
}
Tagged : / /

Powershell Connection Examples

In this post I’m going to show you a few different ways to connect to thing using Powershell. I will make other posts that go into more detail and explain each one but this is more of a reference post. I will probably update this post in the future to include more but this current list includes Powershell Connection Examples for: Active Directory (on-prem), AzureAD, Exchange (on-prem), Exchange Online, vCenter and SCCM. Like all my posts I’m not claiming these are the only ways but these are the ways I use and they work. For these you may need to set you execution policy for these to work:

Set-ExecutionPolicy -ExecutionPolicy Bypass
Active Directory (On-Prem)
#set Variable for which Domain Controller to connect to
$Domain_Controller = "MyDC1"

#Check for Creds and ask for them if they aren't found
if (!($Creds)) {$Creds = get-credential -Message "Enter your Domain Admin Creds"}

#Connect to Domain Controller and import a Active Directory Session
$session = New-PSSession -ComputerName $Domain_Controller -Credential $creds
Invoke-Command $session -Scriptblock { Import-Module ActiveDirectory }
Import-PSSession -Session $session -module ActiveDirectory

Test Command

Get-ADuser username
Example of Active Directory Connection with Powershell
Example of Active Directory Connection with Powershell
AzureAD (MSOL)

For this you need to have the the MSOnline module installed you can get it by running:

Install-Module MSOnline -verbose

There are two ways to run this and it depends on if you have MFA setup and Trusted locations:
Option 1 –
If you do NOT have MFA setup OR you have MFA setup but you are logging in from a “Trusted Location”

#Check for Creds and ask for them if they aren't found
if (!($365Creds)) {$365Creds = get-credential -Message "Enter your Office365 Admin Creds"}

#Make the connection
Connect-MsolService -Credential $365Creds

Option 2 – If you have MFA on and aren’t at a “Trusted Location”

Connect-MsolService


Test Connection

Get-MsolUser -UserPrincipalName user@domain.com
Example of Azure AD Connection with Powershell
Example of Azure AD Connection with Powershell
Exchange (On-Prem)
#Set Exchange Server Name
$Exc_Server = "ExchangeServerName"

#Check for Creds and ask for them if they aren't found
if (!($Creds)) {$Creds = get-credential -Message "Enter your Domain Admin Creds"}

$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri http://$Exc_Server/PowerShell/ -Authentication Kerberos -Credential $creds

Import-PSSession $Session

Test Command

Get-Mailbox username
Example of Exchange Connection with Powershell
Example of Exchange Connection with Powershell
Exchange Online

For this you need the ExchangeOnlineManagement module installed. To install it run:

Install-Module ExchangeOnlineManagement

To connect use this:

Connect-ExchangeOnline

Test Command:

Get-Mailbox username@domain.com
Example of Exchange Online Connection with Powershell
Example of Exchange Online Connection with Powershell
vCenter

For this you need the VMwarePowercli module installed. to install run:

Install-Module VMware.PowerCLI -AllowClobber

To connect:

If you do not have an SSL certificate on your vCenter you will need to set it to ignore your self signed cert with

Set-PowerCLIConfiguration -InvalidCertificateAction ignore

Next set your vCenter server with this command. Change vCenterServerName to match your vCenter server

#Set vCenter Servername
$vCenter_Server = "vCenterServerName"

Here is the actual connection commands, not need to change anything here. It will bring up a credential box. Enter your vCenter creds in domain\username format.

#Check for Creds and ask for them if they aren't found
if (!($Creds)) {$Creds = get-credential -Message "Enter your vCenter Admin Creds in domain\username format"}

Connect-VIServer -server $vCenter_Server -Credential $creds

Test Command:

get-Cluster
Example of vCenter Connection with Powershell
Example of vCenter Connection with Powershell
SCCM

The last Powershell Connection Example I have for you is SCCM. For this one you need to have the SCCM console installed locally or run this from the SCCM server. The console is specific to the version of SCCM you are running, you can get the console install from here \\SCCMSERVERNAME\SCCMConsoleInstaller\consoleinstaller.exe

To connect:

There are a few things to change in the below. Change SITENAME to your SCCM site name in both places, and change SCCM_Server_Name with your SCCM server name.

#Check for Creds and ask for them if they aren't found
if (!($Creds)) {$Creds = get-credential -Message "Enter your Domain Admin Creds"}

Import-Module 'C:\Program Files (x86)\Microsoft Configuration Manager\AdminConsole\bin\ConfigurationManager.psd1'
New-PSDrive -Credential $Creds -Name "SITENAME" -PSProvider "AdminUI.PS.Provider\CMSite" -Root "SCCM_Server_name" -Description "Primary site"
Set-Location SITENAME:

Test command:

Get-CMSite
Example of SCCM Connection with Powershell
Example of SCCM Connection with Powershell
Azure

Connecting to Azure is similar to AzureAD or Exchange online. First, you need the module. Once it is installed, you can now connect. One important thing to note is this AZ module is newer. If you have used the Azure or AzureRM modules in the past you need to remove them with uninstall-module.

Uninstall AzureRM:

Uninstall-AzureRm

Install Module:

 Install-Module az -AllowClobber

Once the module is install you can now connect.

Connect to Azure:

Connect-AzAccount

This will open another window where you can sign into Azure using your credentials.

Test Command:

Get-AzSubscription
Azure Connection Example
Tagged : / / / /