I'm using the following powershell function to list the software installed on remote computers and export the data into various formats such as CSV, GridView or console :
Function Get-InstalledApplication
{
Param(
[Parameter(Mandatory=$true)]
[string[]]$Computername,
[String[]]$OutputType,
[string[]]$outpath
)
#Registry Hives
$Object =#()
$excludeArray = ("Security Update for Windows",
"Update for Windows",
"Update for Microsoft .NET",
"Security Update for Microsoft",
"Hotfix for Windows",
"Hotfix for Microsoft .NET Framework",
"Hotfix for Microsoft Visual Studio 2007 Tools",
"Microsoft Visual C++ 2010",
"cwbin64a",
"Hotfix")
[long]$HIVE_HKROOT = 2147483648
[long]$HIVE_HKCU = 2147483649
[long]$HIVE_HKLM = 2147483650
[long]$HIVE_HKU = 2147483651
[long]$HIVE_HKCC = 2147483653
[long]$HIVE_HKDD = 2147483654
Foreach($EachServer in $Computername){
$Query = Get-WmiObject -ComputerName $Computername -query "Select AddressWidth, DataWidth,Architecture from Win32_Processor"
foreach ($i in $Query)
{
If($i.AddressWidth -eq 64){
$OSArch='64-bit'
}
Else{
$OSArch='32-bit'
}
}
Switch ($OSArch)
{
"64-bit"{
$RegProv = GWMI -Namespace "root\Default" -list -computername $EachServer| where{$_.Name -eq "StdRegProv"}
$Hive = $HIVE_HKLM
$RegKey_64BitApps_64BitOS = "Software\Microsoft\Windows\CurrentVersion\Uninstall"
$RegKey_32BitApps_64BitOS = "Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall"
$RegKey_32BitApps_32BitOS = "Software\Microsoft\Windows\CurrentVersion\Uninstall"
#############################################################################
# Get SubKey names
$SubKeys = $RegProv.EnumKey($HIVE, $RegKey_64BitApps_64BitOS)
# Make Sure No Error when Reading Registry
if ($SubKeys.ReturnValue -eq 0)
{ # Loop through all returned subkeys
ForEach ($Name in $SubKeys.sNames)
{
$SubKey = "$RegKey_64BitApps_64BitOS\$Name"
$ValueName = "DisplayName"
$ValuesReturned = $RegProv.GetStringValue($Hive, $SubKey, $ValueName)
$AppName = $ValuesReturned.sValue
$Version = ($RegProv.GetStringValue($Hive, $SubKey, "DisplayVersion")).sValue
$Publisher = ($RegProv.GetStringValue($Hive, $SubKey, "Publisher")).sValue
$donotwrite = $false
if($AppName.length -gt "0"){
Foreach($exclude in $excludeArray)
{
if($AppName.StartsWith($exclude) -eq $TRUE)
{
$donotwrite = $true
break
}
}
if ($donotwrite -eq $false)
{
$Object += New-Object PSObject -Property #{
Application = $AppName;
Architecture = "64-BIT";
ServerName = $EachServer;
Version = $Version;
Publisher= $Publisher;
}
}
}
}}
#############################################################################
$SubKeys = $RegProv.EnumKey($HIVE, $RegKey_32BitApps_64BitOS)
# Make Sure No Error when Reading Registry
if ($SubKeys.ReturnValue -eq 0)
{
# Loop Through All Returned SubKEys
ForEach ($Name in $SubKeys.sNames)
{
$SubKey = "$RegKey_32BitApps_64BitOS\$Name"
$ValueName = "DisplayName"
$ValuesReturned = $RegProv.GetStringValue($Hive, $SubKey, $ValueName)
$AppName = $ValuesReturned.sValue
$Version = ($RegProv.GetStringValue($Hive, $SubKey, "DisplayVersion")).sValue
$Publisher = ($RegProv.GetStringValue($Hive, $SubKey, "Publisher")).sValue
$donotwrite = $false
if($AppName.length -gt "0"){
Foreach($exclude in $excludeArray)
{
if($AppName.StartsWith($exclude) -eq $TRUE)
{
$donotwrite = $true
break
}
}
if ($donotwrite -eq $false)
{
$Object += New-Object PSObject -Property #{
Application = $AppName;
Architecture = "32-BIT";
ServerName = $EachServer;
Version = $Version;
Publisher= $Publisher;
}
}
}
}
}
} #End of 64 Bit
######################################################################################
###########################################################################################
"32-bit"{
$RegProv = GWMI -Namespace "root\Default" -list -computername $EachServer| where{$_.Name -eq "StdRegProv"}
$Hive = $HIVE_HKLM
$RegKey_32BitApps_32BitOS = "Software\Microsoft\Windows\CurrentVersion\Uninstall"
#############################################################################
# Get SubKey names
$SubKeys = $RegProv.EnumKey($HIVE, $RegKey_32BitApps_32BitOS)
# Make Sure No Error when Reading Registry
if ($SubKeys.ReturnValue -eq 0)
{ # Loop Through All Returned SubKEys
ForEach ($Name in $SubKeys.sNames)
{
$SubKey = "$RegKey_32BitApps_32BitOS\$Name"
$ValueName = "DisplayName"
$ValuesReturned = $RegProv.GetStringValue($Hive, $SubKey, $ValueName)
$AppName = $ValuesReturned.sValue
$Version = ($RegProv.GetStringValue($Hive, $SubKey, "DisplayVersion")).sValue
$Publisher = ($RegProv.GetStringValue($Hive, $SubKey, "Publisher")).sValue
if($AppName.length -gt "0"){
$Object += New-Object PSObject -Property #{
Application = $AppName;
Architecture = "32-BIT";
ServerName = $EachServer;
Version = $Version;
Publisher= $Publisher;
}
}
}}
}#End of 32 bit
} # End of Switch
}
#$AppsReport
$column1 = #{expression="ServerName"; width=15; label="Name"; alignment="left"}
$column2 = #{expression="Architecture"; width=10; label="32/64 Bit"; alignment="left"}
$column3 = #{expression="Application"; width=80; label="Application"; alignment="left"}
$column4 = #{expression="Version"; width=15; label="Version"; alignment="left"}
$column5 = #{expression="Publisher"; width=30; label="Publisher"; alignment="left"}
if ($outputType -eq "Console")
{
"#"*80
"Installed Software Application Report"
"Number of Installed Application count : $($object.count)"
"Generated $(get-date)"
"Generated from $(gc env:computername)"
"#"*80
$object |Format-Table $column1, $column2, $column3 ,$column4, $column5
}
elseif ($OutputType -eq "GridView")
{
$object|Out-GridView
}
elseif ($OutputType -eq "CSV")
{
[string]$FileDS = Get-Date -Format "yyyyMMdd"
[string]$outFile = $outpath+'\'+$computername+'_'+$FileDS+'.csv'
New-Item -ItemType file $outfile -Force
$object | export-csv -path $outfile
}
else
{
write-host " Invalid Output Type $OutputType"
}
}
I'm also using the code to exclude some application from a list. I have a problem on PCs running a 32-bit version of Windows (tested on a 32-bit Windows 7) : applications excluded from this list still appear in the output. No problem on 64-bit systems however.
Ideas of what's wrong ?
Thanks
Related
$isDebug currently exists as a variable in the CleanStaleDevices function,
I need to make the variable into a optional parameter with a default value of $false.
Add the same parameter to the remaining functions.
I would like to set $isDebug as a variable at the initial function call, where CleanStaleDevices is called, then you can pass the value you set down the chain to the other functions. However, if you want to isolate debugging to an individual function you can override.
I tried converting $idDebug to a function itself but it didn't behave the way I was expecting
`function CleanStaleDevices($numDays) {
##Set debug flag
$isDebug = $true
#######Begin
Connect-AzureAD
###Get-AzureADDevice to get list of devices stale by 3 months and make sure devices aren't autopilot managed###
$dt = (Get-Date).AddDays($numDays)
$devices = Get-AzureADDevice -All:$true | Where-Object { $_.ApproximateLastLogonTimeStamp -le $dt } | select-object -Property AccountEnabled, DeviceId, DeviceOSType, DeviceOSVersion, DisplayName, DeviceTrustType, IsManaged, ApproximateLastLogonTimestamp
$fileNameDate = Get-Date -format "yyyyddMM"
$fileNameMiddle = "_AAD_Stale_Devices-"
##Get Intune devices
$intuneDevicesFileName = "$($fileNameDate)$($fileNameMiddle)Intune.csv"
$devices | ? { $_.IsManaged -eq $true } | Export-Csv c:\temp\$intuneDevicesFileName -NoTypeInformation
#######OUTSIDE ACTION#######
##Use Invoke-Device script to retire devices from generated CSV
##https://www.niallbrady.com/2017/08/23/getting-started-with-microsoft-graph-and-using-powershell-to-automate-things-in-intune/
#######END##################
$confirmation = Read-Host "Have you retired the detected Intune devices (y/n)?"
if ($confirmation -eq 'y') {
$hybridDevicesFileName = "$($fileNameDate)$($fileNameMiddle)Hybrid.csv"
$devices | ? { $_.DeviceTrustType -eq "ServerAd" } | Export-Csv c:\temp\$hybridDevicesFileName -NoTypeInformation
#######OUTSIDE ACTION#######
##Use DisableADComputersFromCSV.ps1 to disable the devices generated in the hybrid device csv
#######END##################
$confirmation = Read-Host "Have you disabled the on prem devices and ran a directory sync (y/n)?"
if ($confirmation -eq 'y') {
# proceed
###We don't have to open a new connection for each loop of the devices
$Connection = New-Object System.Data.SQLClient.SQLConnection
$serverName = "XXXXXXX"
$databaseName = "XXXXXXX"
$Connection.ConnectionString = "server='$serverName';database='$databaseName';trusted_connection=true;"
$Connection.Open()
if ($isDebug -eq $true) {
##debug array
$devicesweredisabled = #()
}
##Sort on DeviceTrustType so that AD Joined devices are disabled before AD registered
$devices = $devices | Sort-Object DeviceTrustType
foreach ($device in $devices) {
#if device is enabled
if ($device.AccountEnabled -eq $true) {
if ($isDebug -eq $true) {
##write to debug array
$devicesweredisabled += $device
Write-Host "Disable device" $device.DeviceId $device.DisplayName $device.AccountEnabled
}
else {
##don't disable Hybrid devices since they were disabled earlier
if ($_.DeviceTrustType -ne "ServerAd")
{
Write-Host "DISABLE " $device.DisplayName "with trust type" $device.DeviceTrustType
#$device.AccountEnabled = $false
}
##Check to see if device is in table already, if so check how long its been there
$disabledDateTimeValue = CheckIfDeviceISInTable $device.DeviceId $Connection
if ($null -eq $disabledDateTimeValue) {
##Device hasn't been added to table
WriteDisabledAADDevice $device.DeviceId $device.DisplayName $isDebug $Connection
}
}
}
else {
#device is not enabled
##Check to see if device is in table already, if so check how long its been there
$disabledDateTimeValue = CheckIfDeviceISInTable $device.DeviceId $Connection
if ($null -eq $disabledDateTimeValue) {
##Device is already disabled so we need to add it to the table for time tracking
WriteDisabledAADDevice $device.DeviceId $device.DisplayName $isDebug $Connection
}
else {
$numDaysSince = (Get-Date) - [datetime]$disabledDateTimeValue | % days
if ($numDaysSince -ge 90) {
if ($isDebug -eq $true) {
Write-Host "DEVICE WAS DELETED" $device.DeviceId $device.DisplayName $device.AccountEnabled
}
else {
Write-Host "DELETED..."
#device has been in table for 90 days so delete
##delete device
}
}
}
}
}
$Connection.Close()
if ($isDebug -eq $true) {
$devicesweredisabled | export-csv c:\temp\devicelist-olderthan-x-days-disabledtest.csv -NoTypeInformation
}
}
else{
Exit
}
}
else{
Exit
}
}
function WriteDisabledAADDevice {
Param(
[parameter(position = 3)]
$conn,
[parameter(position = 2)]
$logOnly,
[parameter(position = 1)]
$displayName,
[parameter(position = 0)]
$deviceId
)
$newId = (New-Guid).Guid
$disableDateTime = (Get-Date)
$Command = New-Object System.Data.SQLClient.SQLCommand
$Command.Connection = $conn
$tableName = "DisabledAADDevice"
$insertquery = "
INSERT INTO $tableName
([DisabledAADDeviceId],[AADDeviceId],[DiplayName],[DisabledDateTime])
VALUES
('$newId','$deviceId','$displayName','$disableDateTime')"
if ($logOnly -eq $true) {
Write-Host $insertquery
}
else {
$Command.CommandText = $insertquery
$Command.ExecuteNonQuery()
}
}
function CheckIfDeviceISInTable {
Param(
[parameter(position = 1)]
$conn,
[parameter(position = 0)]
$deviceId
)
$Command = New-Object System.Data.SQLClient.SQLCommand
$Command.Connection = $conn
$tableName = "DisabledAADDevice"
$selectQuery = "
Select [DisabledDateTime] FROM $tableName
WHERE [AADDeviceId] = '$deviceId'"
$Command.CommandText = $selectQuery
$resultValue = $Command.ExecuteScalar()
return $resultValue
}
CleanStaleDevices(-365)`
This only prints the last server in the list, I'm looking to get all servers and print to screen
$machines = (Get-BrokerMachine -AdminAddress $adminaddress -DesktopGroupName $deliverygroup | Select-Object DNSname).DNSname
foreach($machine in $machines){
$machinelist = Get-BrokerMachine -HostedMachineName $machine
if($machinelist.InMaintenanceMode -eq $true){
$status = "$machine is in maintenance mode"
}else {
$status = "$machine is not in maintenance mode"
}
}
Write-Host $status
Here is a more PowerShell-like approach (not tested):
Get-BrokerMachine -AdminAddress $adminaddress -DesktopGroupName $deliverygroup | ForEach-Object {
$machineName = $_.DNSName
[PSCustomObject] #{
"MachineName" = $machineName
"MaintenanceMode" = (Get-BrokerMachine -HostedMachineName $machine).InMaintenanceMode
}
} | Export-Csv "C:\whatever\results.csv" -NoTypeInformation
$Status is constantly being overwritten by the current machine in your list.
You're looking for:
$Status+=
As opposed to:
$Status=
You'll also want to explicitly state that $Status will be an array at the beginning like so:
$Status=#()
Or when you create the variable and omit the line at the beginning.
[array]$Status +=
Otherwise, you'll get results that run together as it will be treated as a [String]
another funky mode :
function get-BrokerMachineMode
{
param (
[Parameter(Mandatory = $true)]
[string[]]$machines
)
begin
{
$ErrorActionPreference = 'Stop'
Add-Type -Language CSharp #"
public class BrokenBroker {
qpublic System.String MachineName;
public System.String MaintenanceMode;
public BrokenBroker (string MachineName, string MaintenanceMode)
{
this.MachineName = MachineName;
this.MaintenanceMode = IsInMaintenanceMode;
}
}
"#
$status = #()
Write-Verbose "Created objects..."
}
process
{
try
{
$machines = (Get-BrokerMachine -AdminAddress $adminaddress `
-DesktopGroupName $deliverygroup | Select-Object DNSname).DNSname
foreach ($machine in $machines)
{
Write-Verbose "Checking machine: $machine"
$machinelist = Get-BrokerMachine -HostedMachineName $machine
if ($machinelist.InMaintenanceMode -eq $true)
{
$status += New-Object BrokenBroker($machine, $true)
}
else
{
$status += New-Object BrokenBroker($machine, $false)
}
}
}
catch
{
Write-Error $error[0].Exception.Message
}
$status
}
end
{
Write-Verbose "Done"
}
}
this is a function you just must to load then you can launch it just by using this command:
$computers = get-content = {PATH TO TXT FILE}
$list = get-BrokerMachineMode -machines $computers -Verbose
I am trying to create an script to get the certificate expiry date for an websites remotely for multiple servers. I have an script which is working for single server (Need to login into server and doing execution), I need to run this remotely for multiple servers. How can i modify this script to execute for multiple servers remotely. Please advice.
$servers = get-content D:\Certificate.txt
$DaysToExpiration = 60 #change this once it's working
$expirationDate = (Get-Date).AddDays($DaysToExpiration)
foreach ($server in $servers)
{
$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
}
}
Inspired by https://iamoffthebus.wordpress.com/2014/02/04/powershell-to-get-remote-websites-ssl-certificate-expiration/ I use following script:
$minimumCertAgeDays = 60
$timeoutMilliseconds = 10000
$urls = get-content .\check-urls.txt
#disabling the cert validation check. This is what makes this whole thing work with invalid certs...
[Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}
foreach ($url in $urls)
{
Write-Host Checking $url -f Green
$req = [Net.HttpWebRequest]::Create($url)
$req.Timeout = $timeoutMilliseconds
$req.AllowAutoRedirect = $false
try {$req.GetResponse() |Out-Null} catch {Write-Host Exception while checking URL $url`: $_ -f Red}
$certExpiresOnString = $req.ServicePoint.Certificate.GetExpirationDateString()
#Write-Host "Certificate expires on (string): $certExpiresOnString"
[datetime]$expiration = [System.DateTime]::Parse($req.ServicePoint.Certificate.GetExpirationDateString())
#Write-Host "Certificate expires on (datetime): $expiration"
[int]$certExpiresIn = ($expiration - $(get-date)).Days
$certName = $req.ServicePoint.Certificate.GetName()
$certPublicKeyString = $req.ServicePoint.Certificate.GetPublicKeyString()
$certSerialNumber = $req.ServicePoint.Certificate.GetSerialNumberString()
$certThumbprint = $req.ServicePoint.Certificate.GetCertHashString()
$certEffectiveDate = $req.ServicePoint.Certificate.GetEffectiveDateString()
$certIssuer = $req.ServicePoint.Certificate.GetIssuerName()
if ($certExpiresIn -gt $minimumCertAgeDays)
{
Write-Host Cert for site $url expires in $certExpiresIn days [on $expiration] -f Green
}
else
{
Write-Host WARNING: Cert for site $url expires in $certExpiresIn days [on $expiration] -f Red
Write-Host Threshold is $minimumCertAgeDays days. Check details:`nCert name: $certName -f Red
Write-Host Cert public key: $certPublicKeyString -f Red
Write-Host Cert serial number: $certSerialNumber`nCert thumbprint: $certThumbprint`nCert effective date: $certEffectiveDate`nCert issuer: $certIssuer -f Red
}
Write-Host
rv req
rv expiration
rv certExpiresIn
}
Alternatively, you might find this advanced script useful:
you can switch between report output as Text, Html or PSObject
use the script with urls (parameter array) or with input file for urls or with pipeline input
improved stability: correctly handle missing certificates on HTTP connections
just put the code into a file like Check-ExpiringSslCerts.ps1
Here the advanced script code:
[CmdletBinding(DefaultParametersetname="URLs in text file")]
Param(
[ValidateSet('Text','Html','PSObject')]
[string]$ReportType = 'Text',
[int]$MinimumCertAgeDays = 60,
[int]$TimeoutMilliseconds = 10000,
[parameter(Mandatory=$false,ParameterSetName = "URLs in text file")]
[string]$UrlsFile = '.\check-urls.txt',
[parameter(Mandatory=$false,ParameterSetName = "List of URLs",
ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
[string[]]$Urls
)
Begin
{
[string[]]$allUrls = #()
$returnData = #()
[bool]$ProcessedInputPipeLineByArrayItem = $false
function CheckUrl ([string]$url, [array]$returnData)
{
[string]$details = $null
if ($ReportType -eq "Html")
{
$stringHtmlEncoded = [System.Web.HttpUtility]::HtmlEncode($url)
Write-Host "<tr><td>$stringHtmlEncoded</td>"
}
if ($ReportType -eq "Text") { Write-Host Checking $url }
$req = [Net.HttpWebRequest]::Create($url)
$req.Timeout = $timeoutMilliseconds
$req.AllowAutoRedirect = $false
try
{
$req.GetResponse() |Out-Null
if ($req.ServicePoint.Certificate -eq $null) {$details = "No certificate in use for connection"}
}
catch
{
$details = "Exception while checking URL $url`: $_ "
}
if ($details -eq $null -or $details -eq "")
{
$certExpiresOnString = $req.ServicePoint.Certificate.GetExpirationDateString()
#Write-Host "Certificate expires on (string): $certExpiresOnString"
[datetime]$expiration = [System.DateTime]::Parse($req.ServicePoint.Certificate.GetExpirationDateString())
#Write-Host "Certificate expires on (datetime): $expiration"
[int]$certExpiresIn = ($expiration - $(get-date)).Days
$certName = $req.ServicePoint.Certificate.GetName()
$certPublicKeyString = $req.ServicePoint.Certificate.GetPublicKeyString()
$certSerialNumber = $req.ServicePoint.Certificate.GetSerialNumberString()
$certThumbprint = $req.ServicePoint.Certificate.GetCertHashString()
$certEffectiveDate = $req.ServicePoint.Certificate.GetEffectiveDateString()
$certIssuer = $req.ServicePoint.Certificate.GetIssuerName()
if ($certExpiresIn -gt $minimumCertAgeDays)
{
if ($ReportType -eq "Html")
{
Write-Host "<td>OKAY</td><td>$certExpiresIn</td><td>$expiration</td><td> </td></tr>"
}
if ($ReportType -eq "Text")
{
Write-Host OKAY: Cert for site $url expires in $certExpiresIn days [on $expiration] -f Green
}
if ($ReportType -eq "PSObject")
{
$returnData += new-object psobject -property #{Url = $url; CheckResult = "OKAY"; CertExpiresInDays = [int]$certExpiresIn; ExpirationOn = [datetime]$expiration; Details = [string]$null}
}
}
else
{
$details = ""
$details += "Cert for site $url expires in $certExpiresIn days [on $expiration]`n"
$details += "Threshold is $minimumCertAgeDays days. Check details:`n"
$details += "Cert name: $certName`n"
$details += "Cert public key: $certPublicKeyString`n"
$details += "Cert serial number: $certSerialNumber`n"
$details += "Cert thumbprint: $certThumbprint`n"
$details += "Cert effective date: $certEffectiveDate`n"
$details += "Cert issuer: $certIssuer"
if ($ReportType -eq "Html")
{
Write-Host "<td>WARNING</td><td>$certExpiresIn</td><td>$expiration</td>"
$stringHtmlEncoded = [System.Web.HttpUtility]::HtmlEncode($details) -replace "`n", "<br />"
Write-Host "<tr><td>$stringHtmlEncoded</td></tr>"
}
if ($ReportType -eq "Text")
{
Write-Host WARNING: $details -f Red
}
if ($ReportType -eq "PSObject")
{
$returnData += new-object psobject -property #{Url = $url; CheckResult = "WARNING"; CertExpiresInDays = [int]$certExpiresIn; ExpirationOn = [datetime]$expiration; Details = $details}
}
rv expiration
rv certExpiresIn
}
}
else
{
if ($ReportType -eq "Html")
{
Write-Host "<td>ERROR</td><td>N/A</td><td>N/A</td>"
$stringHtmlEncoded = [System.Web.HttpUtility]::HtmlEncode($details) -replace "`n", "<br />"
Write-Host "<tr><td>$stringHtmlEncoded</td></tr>"
}
if ($ReportType -eq "Text")
{
Write-Host ERROR: $details -f Red
}
if ($ReportType -eq "PSObject")
{
$returnData += new-object psobject -property #{Url = $url; CheckResult = "ERROR"; CertExpiresInDays = $null; ExpirationOn = $null; Details = $details}
}
}
if ($ReportType -eq "Text") { Write-Host }
rv req
return $returnData
}
#disabling the cert validation check. This is what makes this whole thing work with invalid certs...
[Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}
if ($ReportType -eq "Html")
{
Write-Host "<table><tr><th>URL</th><th>Check result</th><th>Expires in days</th><th>Expires on</th><th>Details</th></tr>"
Add-Type -AssemblyName System.Web
}
}
Process
{
if ($_ -ne $null)
{
CheckUrl $_ $returnData
$ProcessedInputPipeLineByArrayItem = $true
}
}
End
{
if ($ProcessedInputPipeLineByArrayItem -eq $false)
{
if ($Urls -eq $null)
{
$allUrls = get-content $UrlsFile
}
else
{
$allUrls = $Urls
}
foreach ($url in $allUrls)
{
$returnData = CheckUrl $url $returnData
}
}
if ($ReportType -eq "Html") { Write-Host "</table>" }
if ($ReportType -eq "PSObject") { return $returnData }
}
Output might look like e.g.:
"http://www.doma.com", "https://www.domb.com" | .\Check-ExpiringSslCerts.ps1 -ReportType PSObject | ft
Url ExpirationOn CertExpiresInDays CheckResult Details
--- ------------ ----------------- ----------- -------
http://www.doma.com ERROR No certificate in use for connection
https://www.domb.com 18.11.2017 09:33:00 87 OKAY
Put the whole code you've wrote in a script-block, in order to do so, just add at the beginning this code:
$sb = {
and at the bottom of your code add:
}
Once you have this script-block, you can run this script on the servers remotely using these commands:
$cred = Get-Credential
$servers = get-content D:\Certificate.txt
Invoke-Command -Credential $cred -ComputerName $servers -ScriptBlock $SB
Hope it helped!
Your code snippets are helpful but they will throw an error on HTTP errors -- that is the nature of the underlying .NET HttpWebRequest object to panic on everything that's not code 200.
So, if you're testing, say, API endpoints that only accept POST HTTP verb, your script will fail as it will get a 405 error message. Alternatively, if your URL returns HTTP 404, your script will fail as well. Luckily, you can catch layer 7 errors and capture the required info anyway just patch your code in this manner:
try {$req.GetResponse() | Out-Null } catch {
if ($_.Exception.InnerException.Status -eq 'ProtocolError') {
# saving the info anyway since this is a L7 error, e.g.:
$certExpiresOnString = $req.ServicePoint.Certificate.GetExpirationDateString()
[datetime]$expiration [System.DateTime]::Parse($req.ServicePoint.Certificate.GetExpirationDateString())
$certIssuer = $req.ServicePoint.Certificate.GetIssuerName() # ...
} else {
# this is a real error - timeout, DNS failure etc
Write-Host "$url, $_" -ForegroundColor Red
Continue
}
}
Taken mostly from https://gist.github.com/jstangroome/5945820, although with some changes. The following will obtain the certificate. $certificate.NotBefore and $certificate.NotAfter will then need to be checked.
function GetCertificate([string]$domain, [Int16]$port) {
$certificate = $null
$TcpClient = New-Object -TypeName System.Net.Sockets.TcpClient
$TcpClient.ReceiveTimeout = 1000
$TcpClient.SendTimeout = 1000
try {
$TcpClient.Connect($domain, $port)
$TcpStream = $TcpClient.GetStream()
$Callback = { param($sender, $cert, $chain, $errors) return $true }
$SslStream = New-Object -TypeName System.Net.Security.SslStream -ArgumentList #($TcpStream, $true, $Callback)
try {
$SslStream.AuthenticateAsClient($domain)
$certificate = $SslStream.RemoteCertificate
}
finally {
$SslStream.Dispose()
}
}
finally {
$TcpClient.Dispose()
}
if ($certificate) {
if ($certificate -isnot [System.Security.Cryptography.X509Certificates.X509Certificate2]) {
$certificate = New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Certificate2 -ArgumentList $certificate
}
}
return $certificate
}
I am implementing a custom cloudwatch metric for a windows ec2 instance and here's my script for the same. This metric should report cloudwatch regarding the memory and pagefile details.
[CmdletBinding(DefaultParametersetName="credsfromfile", supportsshouldprocess = $true) ]
param(
[switch]$mem_util,
[switch]$mem_used ,
[switch]$mem_avail,
[switch]$page_used,
[switch]$page_avail,
[switch]$page_util,
[switch]$auto_scale,
[ValidateSet("bytes","kilobytes","megabytes","gigabytes" )]
[string]$memory_units = "none",
[switch]$from_scheduler,
[Parameter(Parametersetname ="credsinline",mandatory=$true)]
[string]$aws_access_id = "",
[Parameter(Parametersetname ="credsinline",mandatory=$true)]
[string]$aws_secret_key = "",
[Parameter(Parametersetname ="credsfromfile")]
[string]$aws_credential_file = [Environment]::GetEnvironmentVariable("AWS_CREDENTIAL_FILE"),
[string]$logfile = $null,
[Switch]$version
)
$ErrorActionPreference = 'Stop'
### Initliaze common variables ###
$accountinfo = New-Object psobject
$wc = New-Object Net.WebClient
$time = Get-Date
[string]$aaid =""
[string]$ask =""
$invoc = (Get-Variable myinvocation -Scope 0).value
$currdirectory = Split-Path $invoc.mycommand.path
$scriptname = $invoc.mycommand.Name
$ver = '1.0.0'
$client_name = 'CloudWatch-PutInstanceDataWindows'
$useragent = "$client_name/$ver"
### Logs all messages to file or prints to console based on from_scheduler setting. ###
function report_message ([string]$message)
{
if($from_scheduler)
{ if ($logfile.Length -eq 0 )
{
$logfile = $currdirectory +"\" +$scriptname.replace('.ps1','.log')
}
$message | Out-File -Append -FilePath $logfile
}
else
{
Write-Host $message
}
}
### Global trap for all exceptions for this script. All exceptions will exit the script.###
trap [Exception] {
report_message ($_.Exception.Message)
Exit
}
if ($version)
{
report_message "$scriptname version $ver"
exit
}
####Test and load AWS sdk
$ProgFilesLoc = (${env:ProgramFiles(x86)}, ${env:ProgramFiles} -ne $null)[0]
$SDKLoc = "$ProgFilesLoc\AWS SDK for .NET\bin\Net35"
if ((Test-Path -PathType Container -Path $SDKLoc) -eq $false) {
$SDKLoc = "C:\Windows\Assembly"
}
$SDKLibraryLocation = dir C:\Windows\Assembly -Recurse -Filter "AWSSDK.dll"
if ($SDKLibraryLocation -eq $null)
{
throw "Please Install .NET sdk for this script to work."
}
else
{
$SDKLibraryLocation = $SDKLibraryLocation.FullName
Add-Type -Path $SDKLibraryLocation
Write-Verbose "Assembly Loaded"
}
### Process parameterset for credentials and adds them to a powershell object ###
switch ($PSCmdlet.Parametersetname)
{
"credsinline" {
Write-Verbose "Using credentials passed as arguments"
if (!($aws_access_id.Length -eq 0 ))
{
$aaid = $aws_access_id
}
else
{
throw ("Value of AWS access key id is not specified.")
}
if (!($aws_secret_key.Length -eq 0 ))
{
$ask = $aws_secret_key
}
else
{
throw "Value of AWS secret key is not specified."
}
}
"credsfromfile"{
if ( Test-Path $aws_credential_file)
{
Write-Verbose "Using AWS credentials file $aws_credential_file"
Get-Content $aws_credential_file | ForEach-Object {
if($_ -match '.*=.*'){$text = $_.split("=");
switch ($text[0].trim())
{
"AWSAccessKeyId" {$aaid= $text[1].trim()}
"AWSSecretKey" { $ask = $text[1].trim()}
}}}
}
else {throw "Failed to open AWS credentials file $aws_credential_file"}
}
}
if (($aaid.length -eq 0) -or ($ask.length -eq 0))
{
throw "Provided incomplete AWS credential set"
}
else
{
Add-Member -membertype noteproperty -inputobject $accountinfo -name "AWSSecretKey" -value $ask
Add-Member -membertype noteproperty -inputobject $accountinfo -name "AWSAccessKeyId" -value $aaid
Remove-Variable ask; Remove-Variable aaid
}
### Check if atleast one metric is requested to report.###
if ( !$mem_avail -and !$mem_used -and !$mem_util -and !$page_avail -and !$page_used -and !$page_util)
{
throw "Please specify a metric to report exiting script"
}
### Avoid a storm of calls at the beginning of a minute.###
if ($from_scheduler)
{
$rand = new-object system.random
start-sleep -Seconds $rand.Next(20)
}
### Functions that interact with metadata to get data required for dimenstion calculation and endpoint for cloudwatch api. ###
function get-metadata {
$extendurl = $args
$baseurl = "http://169.254.169.254/latest/meta-data"
$fullurl = $baseurl + $extendurl
return ($wc.DownloadString($fullurl))
}
function get-region {
$az = get-metadata("/placement/availability-zone")
return ($az.Substring(0, ($az.Length -1)))
}
function get-endpoint {
$region = get-region
return "https://monitoring." + $region + ".amazonaws.com/"
}
### Function that creates metric data which will be added to metric list that will be finally pushed to cloudwatch. ###
function append_metric {
$metricdata = New-Object Amazon.Cloudwatch.Model.MetricDatum
$metricdata.metricname, $metricdata.Unit, $metricdata.value, $metricdata.Dimensions = $args
$metricdata.Timestamp = $time.ToUniversalTime()
return $metricdata
}
### Function that validates units passed. Default value of Megabytes is used###
function parse-units {
param ([string]$mem_units,
[long]$mem_unit_div)
$units = New-Object psobject
switch ($memory_units.ToLower())
{
"bytes" { $mem_units = "Bytes"; $mem_unit_div = 1}
"kilobytes" { $mem_units = "Kilobytes"; $mem_unit_div = 1kb}
"megabytes" { $mem_units = "Megabytes"; $mem_unit_div = 1mb}
"gigabytes" { $mem_units = "Gigabytes"; $mem_unit_div = 1gb}
default { $mem_units = "Megabytes"; $mem_unit_div = 1mb}
}
Add-Member -InputObject $units -Name "mem_units" -MemberType NoteProperty -Value $mem_units
Add-Member -InputObject $units -Name "mem_unit_div" -MemberType NoteProperty -Value $mem_unit_div
return $units
}
### Function that gets memory stats using WMI###
function get-memory {
begin {}
process {
$mem = New-Object psobject
$units = parse-units
[long]$mem_avail_wmi = (get-WmiObject Win32_OperatingSystem | select -expandproperty FreePhysicalMemory) * 1kb
[long]$total_phy_mem_wmi = get-WmiObject Win32_ComputerSystem | select -expandproperty TotalPhysicalMemory
[long]$mem_used_wmi = $total_phy_mem_wmi - $mem_avail_wmi
Add-Member -InputObject $mem -Name "mem_avail_wmi" -MemberType NoteProperty -Value $mem_avail_wmi
Add-Member -InputObject $mem -Name "total_phy_mem_wmi" -MemberType NoteProperty -Value $total_phy_mem_wmi
Add-Member -InputObject $mem -Name "mem_used_wmi" -MemberType NoteProperty -Value $mem_used_wmi
Add-Member -InputObject $mem -Name "mem_units" -MemberType NoteProperty -Value $units.mem_units
Add-Member -InputObject $mem -Name "mem_unit_div" -MemberType NoteProperty -Value $units.mem_unit_div
write $mem
}
end{}
}
### Function that writes metrics to be piped to next fucntion to push to cloudwatch.###
function create-metriclist {
param (
[parameter(Valuefrompipeline=$true)] $mem_info)
begin{
$group_name = ""
$instance_id = get-metadata("/instance-id")
$auto_scale_group = Get-ASAutoScalingGroup
foreach($as in $auto_scale_group)
{
$flag = 0
$group_name = $as.AutoScalingGroupName
foreach($ins in $as.Instances)
{
Write-Host $ins.InstanceId
Write-Host "`r`n"
if ($ins.InstanceId -eq $instance_id)
{
$flag = 1
break
}
}
if ($flag -eq 1)
{
break
}
Write-Host "---------------------------`r`n"
}
Write-Host $instance_id
Write-Host "`r`n"
Write-Host $group_name
Write-Host "`r`n"
$dimlist = New-Object Collections.Generic.List[Amazon.Cloudwatch.Model.Dimension]
$dims = New-Object Amazon.Cloudwatch.Model.Dimension
if ( $auto_scale)
{
$dims.Name = "AutoScalingGroupName"
$dims.value = $group_name
}
else
{
$dims.Name = "InstanceId"
$dims.value = $instance_id
}
$dimlist.Add($dims)
$pagefilessize = #{}
$pagefileusage = #{}
gwmi Win32_PageFileSetting | ForEach-Object{$pagefilessize[$_.name]=$_.MaximumSize *1mb}
gwmi Win32_PageFileUsage | ForEach-Object{$pagefileusage[$_.name]=$_.currentusage *1mb}
[string[]]$pagefiles = $pagefilessize.keys
}
process{
if ($mem_util)
{
$percent_mem_util= 0
if ( [long]$mem_info.total_phy_mem_wmi -gt 0 ) { $percent_mem_util = 100 * ([long]$mem_info.mem_used_wmi/[long]$mem_info.total_phy_mem_wmi)}
write (append_metric "MemoryUtilization" "Percent" ("{0:N2}" -f $percent_mem_util) $dimlist)
}
if ($mem_used)
{
write (append_metric "MemoryUsed" $mem_info.mem_units ("{0:N2}" -f ([long]($mem_info.mem_used_wmi/$mem_info.mem_unit_div))) $dimlist)
}
if ( $mem_avail)
{
write (append_metric "MemoryAvailable" $mem_info.mem_units ("{0:N2}" -f ([long]($mem_info.mem_avail_wmi/$mem_info.mem_unit_div))) $dimlist)
}
if ($page_avail)
{
for ($i=0; $i -le ($pagefiles.count - 1);$i++)
{
write (append_metric ("pagefileAvailable("+$pagefiles[$i]+")") $mem_info.mem_units ("{0:N2}" -f ((($pagefilessize[$pagefiles[$i]]- ($pagefileusage[$pagefiles[$i]]))/$mem_info.mem_unit_div))) $dimlist)
}
}
if ($page_used)
{
for ($i = 0; $i -le ($pagefiles.count -1); $i++)
{
write (append_metric ("pagefileUsed("+$pagefiles[$i]+")") $mem_info.mem_units ("{0:N2}" -f (($pagefileusage[$pagefiles[$i]])/$mem_info.mem_unit_div)) $dimlist)
}
}
if ($page_util)
{
for ($i=0; $i -le ($pagefiles.count -1);$i++)
{
if($pagefilessize[$pagefiles[$i]] -gt 0 )
{
write (append_metric ("pagefileUtilization("+$pagefiles[$i]+")") "Percent" ("{0:N2}" -f((($pagefileusage[$pagefiles[$i]])*100)/$pagefilessize[$pagefiles[$i]])) $dimlist)
}
}
}
}
end{}
}
### Uses AWS sdk to push metrics to cloudwathc. This finally prints a requestid.###
function put-instancemem {
param (
[parameter(Valuefrompipeline=$true)] $metlist)
begin{
$cwconfig = New-Object Amazon.CloudWatch.AmazonCloudWatchConfig
$cwconfig.serviceURL = get-endpoint
$cwconfig.UserAgent = $useragent
$monputrequest = new-object Amazon.Cloudwatch.Model.PutMetricDataRequest
$response = New-Object psobject
$metricdatalist = New-Object Collections.Generic.List[Amazon.Cloudwatch.Model.MetricDatum]
}
process{
if ($PSCmdlet.shouldprocess($metlist.metricname,"The metric data "+$metlist.value.tostring() +" "+ $metlist.unit.tostring()+" will be pushed to cloudwatch")){
$metricdatalist.add($metlist)
Write-Verbose ("Metricname= " +$metlist.metricname+" Metric Value= "+ $metlist.value.tostring()+" Metric Units= "+$metlist.unit.tostring())
}
}
end{
$monputrequest.namespace = "System/Windows"
if ($metricdatalist.count -gt 0 ) {
$cwclient = New-Object Amazon.Cloudwatch.AmazonCloudWatchClient($accountinfo.AWSAccessKeyId,$accountinfo.AWSSecretKey,$cwconfig)
$monputrequest.metricdata = $metricdatalist
$monresp = $cwclient.PutMetricData($monputrequest)
Add-Member -Name "RequestId" -MemberType NoteProperty -Value $monresp.ResponseMetadata.RequestId -InputObject $response
}
else {throw "No metric data to push to CloudWatch exiting script" }
Write-Verbose ("RequestID: " + $response.RequestId)
}
}
### Pipelined call of fucntions that pushs metrics to cloudwatch.
get-memory | create-metriclist | put-instancemem
When I execute this manually, I am getting an output as shown below:
'UserAgent' is a ReadOnly property.
But when I try get-memory | create-metriclist it is giving me the full details of memory and pagefile. Which means something is going wrong while trying to push to cloudwatch.
Can someone help me?
How can i set the TRANSACTION ISOLATION LEVEL OF COM PLUS components to Any through powershell script.
I am trying with following code
$comAdmin = New-Object -comobject COMAdmin.COMAdminCatalog
$apps = $comAdmin.GetCollection(“Applications”)
$apps.Populate();
foreach($app in $apps)
{
Write-Host $app.Name
if($app.Name -eq $ApplicationName)
{
$components = $apps.GetCollection("Components",$app.key)
$components.Populate()
foreach ($component in $components)
{
$componentName = $component.Name;
Write-Host "*"$componentName;
if($componentName -eq "WDA_DUP.Generic13")
{
$component.Value("TXIsolationLevel")=COMAdminTxIsolationLevelAny;
}
}
}
}
But not getting the purpose solved.Please Help me :)
`
Working script:
$ApplicationID = "{C0D4E2A4-CAC1-47BA-819C-EAB73E978FDF}"
$comAdmin = New-Object -comobject COMAdmin.COMAdminCatalog
$apps = $comAdmin.GetCollection(“Applications”)
$apps.Populate();
foreach($app in $apps)
{
#Write-Host $app.Name
#Write-Host $app.Key
if($app.Key -eq $ApplicationID)
{
Write-Host $app.Name
$components = $apps.GetCollection("Components",$app.key)
$components.Populate()
foreach ($component in $components)
{
$componentName = $component.Name;
Write-Host "Set "$componentName;
$component.Value("TxIsolationLevel")=2
}
$components.SaveChanges()
}
}