Powershell script to find currently bound expiring certificates in IIS - iis

I am trying to get a working script to check for expiring SSL certificates in IIS. There are many similar entries with simply getting the list of expiring installed certificates but I need some extra logic.
I need to know all certificates expiring within x days that are A) currently bound to a website and B) that website must have a state of "Started"
I have certain information gathered (below) but I am having trouble correlating them so they only give me the expiring certs I need. To add to the complexity, I can't simply look for the site name in the CN in the subject of the certificates because there are many hundreds of certs installed and it is not uncommon for 1 or more older certificates for the same site to still be installed. That being said, they have the same subject. I will need to compare thumbprints but getting the thumbprint by simply specifying the site name is proving to be difficult.
Some of the code to gather various relevant details is as follows:
ActiveSites = get-website | where {$_.State -eq "Started"}
$DaysToExpiration = 7
$InstalledCerts = gci cert:\localmachine\my
$ExpiringCerts = $InstalledCerts | Where {(($_.NotAfter - (Get-Date)).Days) -lt $DaysToExpiration}

A list of the certificates bound to websites can be obtained from the IIS: provider:
Get-ChildItem IIS:SSLBindings
Try this:
$DaysToExpiration = 7
$expirationDate = (Get-Date).AddDays($DaysToExpiration)
$sites = Get-Website | ? { $_.State -eq "Started" } | % { $_.Name }
$certs = Get-ChildItem IIS:SSLBindings | ? {
$sites -contains $_.Sites.Value
} | % { $_.Thumbprint }
Get-ChildItem CERT:LocalMachine/My | ? {
$certs -contains $_.Thumbprint -and $_.NotAfter -lt $expirationDate
}

Related

Getting Distribution Groups / Owners w/ PowerShell, but removing entries that have a null or empty owner

I have the lovely job of getting a list of Distribution Groups and their owners from Exchange Online. So far this is working great, but I need to fine tune my output to exclude groups that don't have any owners; and groups that have multiple owners (by returning only the first).
So far this has been fairly difficult as this is sort of my first foray into PShell.
Here's my code:
$job = Get-DistributionGroup | select Name,PrimarySmtpAddress, #{n= "ManagedBy"; e={$_.ManagedBy | Select-Object -First 1 | Where-Object {$_.ManagedBy.Count -eq 0} |foreach {(Get-Mailbox $_).PrimarySMTPAddress}}}
Write-Output $job | ConvertTo-Json
Here's an example of my output:
I basically only want a single string address to be returned. So a single owner of a distro group, no nulls, and only the first address in the collections. (Right now they are blank, I'm probably nuking them with my code- but usually they return 4 or so email addresses but I just want the first person)
Thanks!
By collection you mean the ManagedBy property right? If so, try with { ($_.ManagedBy | Select-Object -First 1 | Get-Mailbox).PrimarySMTPAddress } –
Santiago Squarzon
1 hour ago

Cut variable into multiple Strings

I want to write a script that renames all NICs on a Server 2012 R2.
Currently it looks like this with each one:
$NIC = Get-WMIObject -Class Win32_NetworkAdapter -Filter "NetconnectionID='Embedded LOM 1 Port 1'"
$NIC.NetconnectionID = 'Physical 1'
$NIC.Put()
Now I want to use this for different Servers and therefore I have to get the NetconnectionID from a variable.
So far I have put the NICs into a variable:
$NICS = Get-NetAdapter | select name
Now when just issuing the command $NICS it shows the list of names, but since I want to rename however many of NICs I have individually I have to break the variable down into different strings. It would be awesome if it would even count the amount and then implement my script with an if statement or foreach!
But for now I would be happy with a solution to rename a specific amount (in my case it's four).
$NICS = Get-NetAdapter | select name
for ($i = 0; $i -lt $NICS.Count; $i++) {
Write-Host (($NICS[$i]) -replace("#{name=","") -replace("}",""))
}
That is a good starting point I think.
Use foreach for that:
$NICS = Get-NetAdapter | select name
foreach ($n in $nics)
{
write-host "Nick name " $n.name
}

Active Directory Powershell - forest-wide search script using .csv list of users

I am looking for a bit of help, hope nobody will bash me for being an ignorant.
Not that long ago I became something of an AD admin, organisation is big so the tasks vary. I can easily complete what I require via Powershell or snap-ins in most cases.
However I have a task on my hands that exceed my "creativity". I have a list of over 10 000 users in .csv which I need to look up in on-premises AD if they exist. My two problems are:
-I am very new to scripting and getting increasingly frustrated that I can't comprehend it and make my scripts work as I need them to
-Deadline for this task and other responsibilities give me little time to read more on scripting basics and learn. As such I am in most cases forced to look for script snippets on the web and modify them a bit to meet my needs. This worked up until now as the script I have on my hands is a bit too complex for me.
Biggest problem I was facing so far is creating a forest-wide search. My organization have a single root domain and 4 child domains. When running a simple foreach loop a like the one below:
ForEach ($User in (Import-Csv c:\users\public\users.csv))
{ If (Get-ADUser $User.mail -server GLOBALCATALOGADDRESS:xxxx)
{ Write-Host "User found: $($User.mail)"
}
Else
{ Write-Host "User not found: $($User.mail)"
}
}
It searches only domain to which my computer is connected.
So I managed to find and modify a forest-wide search script and came up with following:
#Get Domain List
$objForest = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest()
$DomainList = #($objForest.Domains | Select-Object Name)
$Domains = $DomainList | foreach {$_.Name}
$User = Import-CSV c:\users\public\users.csv
#Act on each domain
foreach($Domain in ($Domains))
{
Write-Host "Checking $Domain" -fore red
$ADsPath = [ADSI]"LDAP://$Domain"
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher($ADsPath)
#The filter
Foreach($mail in($User))
{
$objSearcher.Filter = "(&(objectCategory=user)(mail=$User.mail))"
$objSearcher.SearchScope = "Subtree"
$colResults = $objSearcher.FindAll()
foreach ($objResult in $colResults)
{
$objArray = $objResult.GetDirectoryEntry()
write-host $objArray.mail
}
}
}
The script seems to be good in its original form (found here: http://powershell.nicoh.me/powershell-1/active-directory/forest-wide-object-searches) and searches well with wildcard and single parameter as filter.
However I have no idea what am I missing to make it search for every email address I have in .csv and to make it return information whether or not user with such mail was found.
Script itself runs but given the time it takes and blank output it feels like it searches for only one user. I am 100% sure that at least one user from the list exists in on-prem AD.
Any suggestions are very welcome.
Thanks for your attention.
[EDIT]
Final script:
#Get Domain List and load user e-mails from file
$objForest = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest()
$DomainList = #($objForest.Domains | Select-Object Name)
$Domains = $DomainList | foreach {$_.Name}
$Users = Import-CSV c:\users\public\users.csv
#Act on each domain
foreach($Domain in ($Domains))
{
Write-Host "Checking $Domain" -fore red
Foreach($mail in ($Users.mail))
{
Get-ADUser -filter {mail -eq $mail} -Server $domain -properties mail | select mail
}
}
Do yourself a favour and download AD Powershell module: http://blogs.msdn.com/b/rkramesh/archive/2012/01/17/how-to-add-active-directory-module-in-powershell-in-windows-7.aspx
You will then be able to simplify your code and run things like this, making your task much clearer:
...
foreach($Domain in ($Domains))
{
Write-Host "Checking $Domain" -fore red
Foreach($mail in ($User.mail))
{
Get-ADUser -filter {mail -eq $mail} -Server $domain -Properties mail |
select-object -ExpandProperty mail
}
}
...
More on AD PS cmdlets: http://technet.microsoft.com/en-us/library/ee617195.aspx
Use -LDAPfilter & point the -Server to GC.
Get-ADUser -Server DC01.Contoso.com:3268
-Ldapfilter "(ObjectClass=user)(mailnickname=David)"
The above command will search the GC DC01.contoso.com for all the users that their Alias/mailnickname is David.
Is is enough to contact the Domain itself instead of a DC of the domain. Thus this shoud also work
get-aduser -Filter {mailnickname -eq "David") -Server contoso.com:3268

Export AD User Properties to CSV

I want to interrogate the client' AD to see which users are missing property values such as telephone number ahead of User Profile Sync in SharePoint 2013, the script works but now I need to add a little "magic" in order to create my csv file... I have added a comment below to indicate where I think this "magic" should go!
# get Users and groups
#Get the User from AD
$domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
$root = $domain.GetDirectoryEntry()
$search = [System.DirectoryServices.DirectorySearcher]$root
#$search.Filter = "(&(objectCategory=User)(samAccountName=$userName))"
# get a list of the users but not the sp_ service accounts.
$search.Filter = "(&(objectCategory=User) (!(name=SP_*)) )"
$search.SearchScope ="subtree"
# determine the properties we want back
$colPropList = "name", "jobTitle", "telephoneNumber","mail", "department" , "thumbnailPhoto"
foreach ($i in $colPropList){$search.PropertiesToLoad.Add($i)}
$result = $search.FindAll()
if ($result -ne $null)
{
foreach ( $entry in $result )
{
# this works though I might have incorrect names for some of the properties
$user = $entry.Properties;
$user.name
$user.department
$user.jobTitle
$user.telephoneNumber
$user.mail
$user.thumbnailPhoto
*# !!!!!!This is where I need help!!!!!
# as my $user is effectively an object then I should be able to to use it to create a an object with Add-Member
# Do I breaker down the $user properties and create another object with name values ???*
foreach ($o in $user)
{
Add-Member -InputObject $psObject -MemberType NoteProperty -Name $o -Value $o
}
}
$psObject | Export-Csv c:\dev\aduserList.csv -NoTypeInformation
}
I'm not familiar with directorysearcher / adsi, but if you're migrating to SharePoint 2013 I'd guess you also have a computer with PowerShell.
In that case you should use Microsofts ActiveDirectory module (installed on servers and through RSAT for clients) if you have a 2008 DC or 2003 with Active Directory Web Service.
You could also use Quest ADRoles Module.
PowerShell cmdlets are much easier to use for AD administration. You could then shorten down your script to one line(this is the ActiveDirectory module from Microsoft):
Get-ADUser -LDAPFilter "(!(name=SP_*))" -Properties Name, Title, OfficePhone, Mail, Department, thumbnailPhoto | Select-Object Name, Title, OfficePhone, Mail, Department, thumbnailPhoto | Export-Csv c:\dev\aduserList.csv -NoTypeInformation
I'm not sure if the thumbnailphoto part works as I haven't used that attribute before.
Something like this should work:
$search.FindAll() | select -Expand Properties |
select #{n='name';e={$_.name}},
#{n='department';e={$_.department}},
#{n='jobTitle';e={$_.jobtitle}},
#{n='telephoneNumber';e={$_.telephonenumber}},
#{n='mail';e={$_.mail}},
#{n='thumbnailPhoto';e={$_.thumbnailphoto}} |
Export-Csv c:\dev\aduserList.csv -NoTypeInformation
Note that the properties used in the expression section of the calculated property (#{n='name';e={expression}}) must be lowercased:
#{n='thumbnailPhoto';e={$_.thumbnailphoto}}
Using the Get-ADUser cmdlet from the ActiveDirectory module as Frode F. suggested is a more convenient way to get the information you want, but it requires that the AD PowerShell module is installed on the computer where it's used, and that the AD Web Services are installed and running on a DC.

PowerShell: retrieve number of applications in AppPool

How to retrieve the number of applications associated with a specific IIS AppPool via PowerShell command?
We can see the associated applications manually using:
Get-Item IIS:\AppPools\AppPoolName
However, if we manually want to select the Applications column, it is not possible. Also, the Applications column is not listed within | Get-Member *.
Why is the column not listed?
How to find the number of applications associated with a specific IIS AppPool using PowerShell?
The trick is: PowerShell established so-called "view definition files" which tell PowerShell how to format objects (e.g. whether the object is formatted as a a list or a table, which columns are displayed, etc.). Those files can be found at C:\Windows\System32\WindowsPowerShell\v1.0 and are all ending in .format.ps1xml.
To answer the original question: The file C:\Windows\System32\WindowsPowerShell\v1.0\Modules\WebAdministration\iisprovider.format.ps1xml contains the view definition for the AppPool type which defines a calculated column looking like this:
<TableColumnItem>
<ScriptBlock>
$pn = $_.Name
$sites = get-webconfigurationproperty "/system.applicationHost/sites/site/application[#applicationPool=`'$pn`'and #path='/']/parent::*" machine/webroot/apphost -name name
$apps = get-webconfigurationproperty "/system.applicationHost/sites/site/application[#applicationPool=`'$pn`'and #path!='/']" machine/webroot/apphost -name path
$arr = #()
if ($sites -ne $null) {$arr += $sites}
if ($apps -ne $null) {$arr += $apps}
if ($arr.Length -gt 0) {
$out = ""
foreach ($s in $arr) {$out += $s.Value + "`n"}
$out.Substring(0, $out.Length - 1)
}
</ScriptBlock>
</TableColumnItem>
This answers why the column itself is not a member of the AppPool type. The second question can be easily answered now extracting the necessary code from the "scriptlet" above:
$applicationsInAppPoolCount = #(Get-WebConfigurationProperty `"/system.applicationHost/sites/site/application[#applicationPool=`'$appPool`'and #path!='/']"` "machine/webroot/apphost" -name path).Count
I dealt with this same issue for many hours until finally arriving at the solution. The answer from D.R. was very helpful but it was not working for me. After some tweaks, I came up with the code below which retrieves the number of applications in an app pool.
I noticed that this part of the code nd #path!='/' threw off the count.
$appPool = "REPLACE ME with a value from your app pool"
#(Get-WebConfigurationProperty "/system.applicationHost/sites/site/application[#applicationPool=`'$appPool`']" "machine/webroot/apphost" -name path).Count
I ended up with the following Code (basically the same as above, but differently formatted)
$appPools = Get-ChildItem –Path IIS:\AppPools
foreach ($apppool in $apppools) {
$appoolName = $apppool.Name
[string] $NumberOfApplications = (Get-WebConfigurationProperty "/system.applicationHost/sites/site/application[#applicationPool='$appoolName']" "machine/webroot/apphost" -name path).Count
Write-Output "AppPool name: $appoolName has $NumberOfApplications applications"
}
I recently came across this post searching for ways to get the active Application Pools. The information provided above was great, but I kept digging to see if there was another way get this information. I was able to find a way to do this through Get-IISSite, which I used the following:
Get-IISSite | Select-Object -ExpandProperty Applications | Select-Object Path,ApplicationPoolName
I tested this on a server that only had one website, but if there are multiple sites on the server, you could also add VirtualDirectories for the Select.
I also had a need to just get a unique list of the Application Pools being used, so I did the following:
$appPoolInfo = Get-IISSite | Select-Object -ExpandProperty Applications | Select-Object Path,ApplicationPoolName
$appPoolInfo | Select-Object -Unique ApplicationPoolName
This gives what you are looking in an array.
Import-Module WebAdministration;
Get-ChildItem IIS:\AppPools >> AppPoolDetails.txt;
$appPoolDetails = Get-Content .\AppPoolDetails.txt;
$w = ($appPoolDetails |Select-String 'State').ToString().IndexOf("State");
$w = $w -1;
$res1 = $appPoolDetails | Foreach {
$i=0;
$c=0; `
while($i+$w -lt $_.length -and $c++ -lt 1) {
$_.Substring($i,$w);$i=$i+$w-1}}
Write-Host "First Column---";
$res1.Trim();
$j = $w + 1;
$w = ($appPoolDetails |Select-String 'Applications').ToString().IndexOf("Applications");
$w = $w -$j;
$res2 = $appPoolDetails | Foreach {
$i=$j;
$c=0; `
while($i+$w -lt $_.length -and $c++ -lt 1) {
$_.Substring($i,$w);$i=$i+$w-1}}
Write-Host "Second Column---";
$res2.Trim();
$lineLength=0
$appPoolDetails | Foreach {
if($lineLength -lt $_.TrimEnd().Length )
{
$lineLength = $_.TrimEnd().Length;
#Write-Host $lineLength;
}
}
$j = ($appPoolDetails | Select-String 'Applications').ToString().IndexOf("Applications");
$w = $lineLength;
$w = $w -$j;
#Write-Host $j $w;
$res3 = $appPoolDetails | Foreach {
$i=$j;
$c=0; `
while($i+$w -lt $_.length -and $c++ -lt 1) {
$_.Substring($i,$w);$i=$i+$w-1}}
Write-Host "Third Column---";
$res3;

Resources