From ccb6c30e57d660397ef7d7d7fd476ed64ae7b577 Mon Sep 17 00:00:00 2001 From: Adam Bertram Date: Wed, 22 May 2024 14:19:08 -0500 Subject: [PATCH 01/19] added Get-EntraIdAppAccessToken --- Entra ID/Get-EntraIdAppAccessToken.ps1 | 187 +++++++++++++++++++++++++ 1 file changed, 187 insertions(+) create mode 100644 Entra ID/Get-EntraIdAppAccessToken.ps1 diff --git a/Entra ID/Get-EntraIdAppAccessToken.ps1 b/Entra ID/Get-EntraIdAppAccessToken.ps1 new file mode 100644 index 0000000..01132a5 --- /dev/null +++ b/Entra ID/Get-EntraIdAppAccessToken.ps1 @@ -0,0 +1,187 @@ +<# +.SYNOPSIS + Authenticates to Microsoft Graph using either a certificate or client secret and retrieves an access token. + +.DESCRIPTION + This script supports two authentication methods for accessing Microsoft Graph: certificate-based and client secret-based. + Depending on the provided parameters, it constructs the appropriate request and retrieves an OAuth 2.0 access token from + Microsoft Identity Platform. + +.PARAMETER Certificate + The X.509 certificate used for certificate-based authentication. This parameter is mandatory for the CertificateAuth parameter set. + +.PARAMETER PfxCertificate + The PFX certificate used for signing the JWT in certificate-based authentication. This parameter is mandatory for the CertificateAuth parameter set. + +.PARAMETER ClientId + The client ID of the application in Azure AD. This parameter is mandatory for the ClientSecretAuth parameter set. + +.PARAMETER ClientSecret + The client secret associated with the application in Azure AD. This parameter is mandatory for the ClientSecretAuth parameter set. + +.PARAMETER Scope + The scope of the access request. The default value is 'https://graph.microsoft.com/.default'. + +.PARAMETER TenantId + The Azure AD tenant ID. This parameter is mandatory. + +.PARAMETER ApplicationId + The application ID of the Azure AD application. This parameter is mandatory. + +.NOTES + For more information on Microsoft identity platform and OAuth 2.0, visit: + https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-client-creds-grant-flow + +.EXAMPLE + PS> $tenantId = (Get-AzContext).Tenant.Id + PS> $appId = (Get-AzADApplication -DisplayName 'someapp').AppId + PS> $pfxCert = get-pfxcertificate -FilePath 'path\to\your\pfx\file.pfx' + PS> $cert = get-pfxcertificate -FilePath 'path\to\your\cert\file.cer' + PS> .\Get-EntraIdAppAccessToken.ps1 -Certificate $cert -PfxCertificate $pfxCert -TenantId $tenantId -ApplicationId $appId + + + This example demonstrates how to use certificate-based authentication to retrieve an access token from Microsoft Graph. + +.EXAMPLE + PS> $tenantId = (Get-AzContext).Tenant.Id + PS> $appId = (Get-AzADApplication -DisplayName 'Entra ID').AppId + PS> .\Get-EntraIdAppAccessToken.ps1 -ClientId $appId -ClientSecret $secureSecret -TenantId $tenantId -ApplicationId $appId + + + This example demonstrates how to use client secret-based authentication to retrieve an access token from Microsoft Graph. +#> +[CmdletBinding()] +param +( + [Parameter(Mandatory, ParameterSetName = 'CertificateAuth')] + [ValidateNotNullOrEmpty()] + [System.Security.Cryptography.X509Certificates.X509Certificate2]$Certificate, + + [Parameter(Mandatory, ParameterSetName = 'CertificateAuth')] + [ValidateNotNullOrEmpty()] + [System.Security.Cryptography.X509Certificates.X509Certificate2]$PfxCertificate, + + [Parameter(Mandatory, ParameterSetName = 'ClientSecretAuth')] + [ValidateNotNullOrEmpty()] + [string]$ClientId, + + [Parameter(Mandatory, ParameterSetName = 'ClientSecretAuth')] + [ValidateNotNullOrEmpty()] + [SecureString]$ClientSecret, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$Scope = 'https://graph.microsoft.com/.default', + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$TenantId, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$ApplicationId +) + +$ErrorActionPreference = 'Stop' + +$httpBody = @{ + client_id = $ApplicationId + scope = $Scope + grant_type = "client_credentials" +} + +$getIrmParams = @{ + ## Dont use tenant name here or you will get "Specified tenant identifier ...... is neither a valid DNS name, nor a valid external domain" + Uri = "https://login.microsoftonline.com/$TenantId/oauth2/v2.0/token" + Method = 'POST' + ContentType = "application/x-www-form-urlencoded" +} + +switch ($PSCmdlet.ParameterSetName) { + 'CertificateAuth' { + # Create base64 hash of certificate + $CertificateBase64Hash = [System.Convert]::ToBase64String($Certificate.GetCertHash()) + + # Create JWT timestamp for expiration + $StartDate = (Get-Date "1970-01-01T00:00:00Z" ).ToUniversalTime() + $JWTExpirationTimeSpan = (New-TimeSpan -Start $StartDate -End (Get-Date).ToUniversalTime().AddMinutes(2)).TotalSeconds + $JWTExpiration = [math]::Round($JWTExpirationTimeSpan, 0) + + # Create JWT validity start timestamp + $NotBeforeExpirationTimeSpan = (New-TimeSpan -Start $StartDate -End ((Get-Date).ToUniversalTime())).TotalSeconds + $NotBefore = [math]::Round($NotBeforeExpirationTimeSpan, 0) + + # Create JWT header + $JWTHeader = @{ + alg = "RS256" + typ = "JWT" + # Use the CertificateBase64Hash and replace/strip to match web encoding of base64 + x5t = $CertificateBase64Hash -replace '\+', '-' -replace '/', '_' -replace '=' + } + + # Create JWT payload + $JWTPayLoad = @{ + # What endpoint is allowed to use this JWT + ## Dont use tenant name here or you will get "Specified tenant identifier ...... is neither a valid DNS name, nor a valid external domain" + aud = "https://login.microsoftonline.com/$TenantId/oauth2/token" + exp = $JWTExpiration # Expiration timestamp + iss = $ApplicationId # Issuer = your application + jti = [guid]::NewGuid() # JWT ID: random guid + nbf = $NotBefore # Not to be used before + sub = $ApplicationId # JWT Subject + } + + # Convert header and payload to base64 + $JWTHeaderToByte = [System.Text.Encoding]::UTF8.GetBytes(($JWTHeader | ConvertTo-Json)) + $EncodedHeader = [System.Convert]::ToBase64String($JWTHeaderToByte) + + $JWTPayLoadToByte = [System.Text.Encoding]::UTF8.GetBytes(($JWTPayload | ConvertTo-Json)) + $EncodedPayload = [System.Convert]::ToBase64String($JWTPayLoadToByte) + + # Join header and Payload with "." to create a valid (unsigned) JWT + $JWT = $EncodedHeader + "." + $EncodedPayload + + # Get the private key object of your certificate + $privKey = ([System.Security.Cryptography.X509Certificates.RSACertificateExtensions]::GetRSAPrivateKey($PfxCertificate)) + + # Define RSA signature and hashing algorithm + $RSAPadding = [Security.Cryptography.RSASignaturePadding]::Pkcs1 + $HashAlgorithm = [Security.Cryptography.HashAlgorithmName]::SHA256 + + # Create a signature of the JWT + $Signature = [Convert]::ToBase64String( + $privKey.SignData([System.Text.Encoding]::UTF8.GetBytes($JWT), $HashAlgorithm, $RSAPadding) + ) -replace '\+', '-' -replace '/', '_' -replace '=' + + # Join the signature to the JWT with "." + $jwt = $JWT + "." + $Signature + + $httpBody += @{ + client_assertion = $jwt + client_assertion_type = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer" + } + + $getIrmParams += @{ + Headers = @{ + Authorization = "Bearer $JWT" + } + Body = $httpBody + } + } + 'ClientSecretAuth' { + $httpBody.client_secret = [System.Net.NetworkCredential]::new("", $ClientSecret).Password + $getIrmParams += @{ + Headers = @{ + Authorization = "Bearer $JWT" + } + Body = $httpBody + } + } + default { + throw "Unrecognized input: [$_]" + } +} + +$Request = Invoke-RestMethod @getIrmParams + +$Request.access_token \ No newline at end of file From 17f596d1148277247d4e4dde0acd995216c5004e Mon Sep 17 00:00:00 2001 From: Adam Bertram Date: Wed, 22 May 2024 14:47:10 -0500 Subject: [PATCH 02/19] added new-sharepointonlinesite --- .../New-SharePointOnlineSite.ps1 | 123 ++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 Microsoft SharePoint Online/New-SharePointOnlineSite.ps1 diff --git a/Microsoft SharePoint Online/New-SharePointOnlineSite.ps1 b/Microsoft SharePoint Online/New-SharePointOnlineSite.ps1 new file mode 100644 index 0000000..9169d61 --- /dev/null +++ b/Microsoft SharePoint Online/New-SharePointOnlineSite.ps1 @@ -0,0 +1,123 @@ +#requires -Modules @{ModuleName="pnp.powershell";ModuleVersion="2.4.0"} +#requires -Modules @{ModuleName="Az.Accounts";ModuleVersion="2.15.1"} + +<# +.SYNOPSIS +Creates a new SharePoint site using PnP PowerShell with either certificate or client secret authentication. + +.DESCRIPTION +This script connects to a SharePoint Online tenant and creates a new site. The connection can be established using either +certificate-based or client secret-based authentication, depending on the provided parameters. + +.PARAMETER Title +The title of the new SharePoint site. + +.PARAMETER Name +The name (URL segment) of the new SharePoint site. + +.PARAMETER Type +The type of the SharePoint site. Valid values are 'TeamSite', 'CommunicationSite', and 'TeamSiteWithoutMicrosoft365Group'. + +.PARAMETER EntraIdTenantId +The Azure AD tenant ID. If logged into Azure AD, this can be retrieved with (Get-AzContext).Tenant.Id. + +.PARAMETER SharePointTenant +The SharePoint tenant name (e.g., "contoso" for "contoso.sharepoint.com"). + +.PARAMETER Owner +An array of owners for the new SharePoint site. + +.PARAMETER PrivateKeyFilePath +The file path to the private key for certificate-based authentication. Required when using the CertificateAuth parameter set. + +.PARAMETER PrivateKeyPassword +The password for the private key. Required when using the CertificateAuth parameter set. + +.PARAMETER ClientId +The client ID for the Azure AD application. + +.PARAMETER ClientSecret +The client secret for client secret-based authentication. Required when using the ClientSecretAuth parameter set. + +.NOTES +Requires the PnP PowerShell module (v2.4.0) and Az.Accounts module (v2.15.1). +For more information on PnP PowerShell, visit: https://pnp.github.io/powershell/ + +.EXAMPLE + + PS> $tenantId = (Get-AzContext).Tenant.Id + PS> $app = Get-AzADApplication -DisplayName "SharePoint Online" + PS> .\New-SharePointOnlineSite.ps1 -Title "New Team Site" -Name "newteamsite" -Type "TeamSite" -EntraIdTenantId $tenantId + -SharePointTenant "contoso" -Owner "user@contoso.com" -ClientId $app.AppId -ClientSecret (ConvertTo-SecureString "your-client-secret" -AsPlainText -Force) + + Creates a new Team Site using client secret authentication. + +.EXAMPLE + + PS> $tenantId = (Get-AzContext).Tenant.Id + PS> $app = Get-AzADApplication -DisplayName "SharePoint Online" + PS> .\New-SharePointOnlineSite.ps1 -Title "New Communication Site" -Name "newcommsite" -Type "CommunicationSite" -EntraIdTenantId $tenantId + -SharePointTenant "contoso" -Owner "user@contoso.com" -ClientId $app.AppId -PrivateKeyFilePath "path\to\privatekey.pfx" + -PrivateKeyPassword (ConvertTo-SecureString "your-password" -AsPlainText -Force) + + Creates a new Communication Site using certificate-based authentication. +#> +[CmdletBinding()] +param( + [Parameter(Mandatory)] + [string]$Title, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$Name, + + [Parameter(Mandatory)] + [ValidateSet('TeamSite', 'CommunicationSite', 'TeamSiteWithoutMicrosoft365Group')] + [string]$Type, + + [Parameter(Mandatory)] + [guid]$EntraIdTenantId, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$SharePointTenant, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string[]]$Owner, + + [Parameter(Mandatory, ParameterSetName = 'CertificateAuth')] + [ValidateNotNullOrEmpty()] + [string]$PrivateKeyFilePath, + + [Parameter(Mandatory, ParameterSetName = 'CertificateAuth')] + [ValidateNotNullOrEmpty()] + [securestring]$PrivateKeyPassword, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$ClientId, + + [Parameter(Mandatory, ParameterSetName = 'ClientSecretAuth')] + [ValidateNotNullOrEmpty()] + [securestring]$ClientSecret +) + +$ErrorActionPreference = 'Stop' + +$connectParams = @{ + ClientId = $ClientId + Tenant = $EntraIdTenantId + Url = "https://$SharePointTenant.sharepoint.com" +} + +if ($PSCmdlet.ParameterSetName -eq 'CertificateAuth') { + $connectParams.CertificatePath = $PrivateKeyFilePath + $connectParams.CertificatePassword = $CertificatePassword +} else { + $connectParams.ClientSecret = $ClientSecret +} + +Connect-PnPOnline @connectParams + +New-PnPSite -Type $Type -Title $Title -Url "https://$SharePointTenant.sharepoint.com/sites/$Name" -Owner ($Owner -join ";") \ No newline at end of file From 9fd873742692ea9855adc31921a870f42a3f6002 Mon Sep 17 00:00:00 2001 From: Adam Bertram Date: Thu, 23 May 2024 15:17:57 -0500 Subject: [PATCH 03/19] added New-ExchangeOnlineMailbox --- .../New-ExchangeOnlineMailbox.ps1 | 437 ++++++++++++++++++ 1 file changed, 437 insertions(+) create mode 100644 Microsoft Exchange Online/New-ExchangeOnlineMailbox.ps1 diff --git a/Microsoft Exchange Online/New-ExchangeOnlineMailbox.ps1 b/Microsoft Exchange Online/New-ExchangeOnlineMailbox.ps1 new file mode 100644 index 0000000..c8874b8 --- /dev/null +++ b/Microsoft Exchange Online/New-ExchangeOnlineMailbox.ps1 @@ -0,0 +1,437 @@ +#requires -Modules @{ModuleName="ExchangeOnlineManagement";ModuleVersion="3.5.0"} + +<# +.SYNOPSIS + Creates a new mailbox in an Exchange environment with various options for user, room, shared, and more. + +.DESCRIPTION + The script wraps around the New-Mailbox cmdlet, providing a rich set of parameters to create different types of mailboxes. + It supports various parameter sets to handle specific scenarios such as creating room mailboxes, shared mailboxes, equipment mailboxes, + and more. The cmdlet also integrates with Microsoft Online Services for federated users and supports remote and inactive mailboxes. + +.PARAMETER ActiveSyncMailboxPolicy + Specifies the ActiveSync mailbox policy to apply to the new mailbox. + +.PARAMETER Alias + Specifies the alias for the new mailbox. This parameter is required. + +.PARAMETER Archive + Specifies whether to create an archive mailbox for the user. + +.PARAMETER Discovery + Specifies that the mailbox is a discovery mailbox. This parameter is mandatory for the Discovery parameter set. + +.PARAMETER DisplayName + Specifies the display name for the new mailbox. + +.PARAMETER EnableRoomMailboxAccount + Specifies whether to enable the account for a room mailbox. This parameter is mandatory for the EnableRoomMailboxAccount parameter set. + +.PARAMETER Equipment + Specifies that the mailbox is an equipment mailbox. This parameter is mandatory for the Equipment parameter set. + +.PARAMETER FederatedIdentity + Specifies the federated identity for a federated user. This parameter is mandatory for the MicrosoftOnlineServicesFederatedUser and FederatedUser parameter sets. + +.PARAMETER FirstName + Specifies the first name of the user for the new mailbox. + +.PARAMETER Force + Forces the command to execute without asking for user confirmation. + +.PARAMETER HoldForMigration + Specifies that the mailbox is being held for migration. This parameter is used with the PublicFolder parameter set. + +.PARAMETER ImmutableId + Specifies the immutable ID for the mailbox. + +.PARAMETER InactiveMailbox + Specifies the inactive mailbox object. Used in various parameter sets that involve inactive or removed mailboxes. + +.PARAMETER Initials + Specifies the initials of the user. + +.PARAMETER IsExcludedFromServingHierarchy + Specifies whether the public folder mailbox is excluded from serving the hierarchy. + +.PARAMETER LastName + Specifies the last name of the user for the new mailbox. + +.PARAMETER MailboxPlan + Specifies the mailbox plan to apply to the new mailbox. Used in various parameter sets. + +.PARAMETER MailboxRegion + Specifies the region for the new mailbox. + +.PARAMETER MicrosoftOnlineServicesID + Specifies the Microsoft Online Services ID for the user. This parameter is mandatory in certain parameter sets. + +.PARAMETER Migration + Specifies that the mailbox is created for migration purposes. This parameter is mandatory for the Migration parameter set. + +.PARAMETER ModeratedBy + Specifies the users who moderate the mailbox. Used in various parameter sets. + +.PARAMETER ModerationEnabled + Specifies whether moderation is enabled for the mailbox. Used in various parameter sets. + +.PARAMETER Name + Specifies the name of the new mailbox. This parameter is mandatory and positional. + +.PARAMETER Office + Specifies the office location of the new mailbox. Used in room and linked room mailboxes. + +.PARAMETER OrganizationalUnit + Specifies the organizational unit (OU) for the new mailbox. + +.PARAMETER Password + Specifies the password for the new mailbox. This parameter is mandatory in certain parameter sets. + +.PARAMETER Phone + Specifies the phone number for the new mailbox. Used in room and linked room mailboxes. + +.PARAMETER PrimarySmtpAddress + Specifies the primary SMTP address for the new mailbox. + +.PARAMETER PublicFolder + Specifies that the mailbox is a public folder mailbox. This parameter is mandatory for the PublicFolder parameter set. + +.PARAMETER RemotePowerShellEnabled + Specifies whether remote PowerShell is enabled for the new mailbox. + +.PARAMETER RemovedMailbox + Specifies the removed mailbox object. Used in various parameter sets involving removed or inactive mailboxes. + +.PARAMETER ResetPasswordOnNextLogon + Specifies whether the user must reset the password on next logon. + +.PARAMETER ResourceCapacity + Specifies the resource capacity for the new mailbox. Used in room and linked room mailboxes. + +.PARAMETER RoleAssignmentPolicy + Specifies the role assignment policy for the new mailbox. + +.PARAMETER Room + Specifies that the mailbox is a room mailbox. This parameter is mandatory for the Room parameter set. + +.PARAMETER RoomMailboxPassword + Specifies the password for the room mailbox account. This parameter is mandatory for the EnableRoomMailboxAccount parameter set. + +.PARAMETER SendModerationNotifications + Specifies how moderation notifications are sent. Used in various parameter sets. + +.PARAMETER Shared + Specifies that the mailbox is a shared mailbox. This parameter is mandatory for the Shared parameter set. + +.PARAMETER TargetAllMDBs + Specifies whether to target all mailbox databases. + +.EXAMPLE + PS> .\New-Mailbox.ps1 -Name "John Doe" -Alias "jdoe" -Password (ConvertTo-SecureString "P@ssw0rd" -AsPlainText -Force) -DisplayName "John Doe" -FirstName "John" -LastName "Doe" -User + + This example creates a new user mailbox for John Doe with the specified parameters. + +.EXAMPLE + PS> .\New-Mailbox.ps1 -Name "ConferenceRoom1" -Alias "confroom1" -Room -RoomMailboxPassword (ConvertTo-SecureString "P@ssw0rd" -AsPlainText -Force) -Office "Head Office" + + This example creates a new room mailbox for a conference room at the Head Office location. + +.EXAMPLE + PS> .\New-Mailbox.ps1 -Name "Shared Mailbox" -Alias "sharedmail" -Shared + + This example creates a new shared mailbox with the specified parameters. +#> + +[CmdletBinding(DefaultParameterSetName = 'User', SupportsShouldProcess = $true, ConfirmImpact = 'Medium')] +param( + [System.Object] + ${ActiveSyncMailboxPolicy}, + + [ValidateNotNullOrEmpty()] + [string] + ${Alias}, + + [switch] + ${Archive}, + + [Parameter(ParameterSetName = 'Discovery', Mandatory = $true)] + [switch] + ${Discovery}, + + [string] + ${DisplayName}, + + [Parameter(ParameterSetName = 'EnableRoomMailboxAccount', Mandatory = $true)] + [bool] + ${EnableRoomMailboxAccount}, + + [Parameter(ParameterSetName = 'Equipment', Mandatory = $true)] + [switch] + ${Equipment}, + + [Parameter(ParameterSetName = 'MicrosoftOnlineServicesFederatedUser', Mandatory = $true)] + [Parameter(ParameterSetName = 'FederatedUser', Mandatory = $true)] + [string] + ${FederatedIdentity}, + + [string] + ${FirstName}, + + [switch] + ${Force}, + + [Parameter(ParameterSetName = 'PublicFolder')] + [switch] + ${HoldForMigration}, + + [string] + ${ImmutableId}, + + [Parameter(ParameterSetName = 'User', ValueFromPipeline = $true)] + [Parameter(ParameterSetName = 'InactiveMailbox', Mandatory = $true, ValueFromPipeline = $true)] + [Parameter(ParameterSetName = 'WindowsLiveCustomDomains', ValueFromPipeline = $true)] + [Parameter(ParameterSetName = 'MicrosoftOnlineServicesID', ValueFromPipeline = $true)] + [Parameter(ParameterSetName = 'WindowsLiveID', ValueFromPipeline = $true)] + [System.Object] + ${InactiveMailbox}, + + [string] + ${Initials}, + + [Parameter(ParameterSetName = 'PublicFolder')] + [bool] + ${IsExcludedFromServingHierarchy}, + + [string] + ${LastName}, + + [Parameter(ParameterSetName = 'MicrosoftOnlineServicesID')] + [Parameter(ParameterSetName = 'RemoteArchive')] + [Parameter(ParameterSetName = 'MicrosoftOnlineServicesFederatedUser')] + [Parameter(ParameterSetName = 'FederatedUser')] + [Parameter(ParameterSetName = 'InactiveMailbox')] + [Parameter(ParameterSetName = 'RemovedMailbox')] + [Parameter(ParameterSetName = 'ImportLiveId')] + [Parameter(ParameterSetName = 'DisabledUser')] + [Parameter(ParameterSetName = 'MailboxPlan')] + [Parameter(ParameterSetName = 'WindowsLiveCustomDomains')] + [Parameter(ParameterSetName = 'WindowsLiveID')] + [Parameter(ParameterSetName = 'User')] + [System.Object] + ${MailboxPlan}, + + [ValidateNotNullOrEmpty()] + [string] + ${MailboxRegion}, + + [Parameter(ParameterSetName = 'EnableRoomMailboxAccount')] + [Parameter(ParameterSetName = 'MicrosoftOnlineServicesFederatedUser', Mandatory = $true)] + [Parameter(ParameterSetName = 'MicrosoftOnlineServicesID', Mandatory = $true)] + [System.Object] + ${MicrosoftOnlineServicesID}, + + [Parameter(ParameterSetName = 'Migration', Mandatory = $true)] + [switch] + ${Migration}, + + [Parameter(ParameterSetName = 'GroupMailbox')] + [Parameter(ParameterSetName = 'RemoteArchive')] + [Parameter(ParameterSetName = 'InactiveMailbox')] + [Parameter(ParameterSetName = 'RemovedMailbox')] + [Parameter(ParameterSetName = 'DisabledUser')] + [Parameter(ParameterSetName = 'ImportLiveId')] + [Parameter(ParameterSetName = 'WindowsLiveCustomDomains')] + [Parameter(ParameterSetName = 'MailboxPlan')] + [Parameter(ParameterSetName = 'MicrosoftOnlineServicesID')] + [Parameter(ParameterSetName = 'WindowsLiveID')] + [Parameter(ParameterSetName = 'Equipment')] + [Parameter(ParameterSetName = 'Room')] + [Parameter(ParameterSetName = 'LinkedRoomMailbox')] + [Parameter(ParameterSetName = 'LinkedWithSyncMailbox')] + [Parameter(ParameterSetName = 'Linked')] + [Parameter(ParameterSetName = 'TeamMailboxITPro')] + [Parameter(ParameterSetName = 'TeamMailboxIW')] + [Parameter(ParameterSetName = 'Shared')] + [Parameter(ParameterSetName = 'User')] + [System.Object] + ${ModeratedBy}, + + [Parameter(ParameterSetName = 'RemoteArchive')] + [Parameter(ParameterSetName = 'InactiveMailbox')] + [Parameter(ParameterSetName = 'RemovedMailbox')] + [Parameter(ParameterSetName = 'DisabledUser')] + [Parameter(ParameterSetName = 'ImportLiveId')] + [Parameter(ParameterSetName = 'WindowsLiveCustomDomains')] + [Parameter(ParameterSetName = 'MailboxPlan')] + [Parameter(ParameterSetName = 'MicrosoftOnlineServicesID')] + [Parameter(ParameterSetName = 'WindowsLiveID')] + [Parameter(ParameterSetName = 'Equipment')] + [Parameter(ParameterSetName = 'Room')] + [Parameter(ParameterSetName = 'LinkedRoomMailbox')] + [Parameter(ParameterSetName = 'LinkedWithSyncMailbox')] + [Parameter(ParameterSetName = 'Linked')] + [Parameter(ParameterSetName = 'TeamMailboxITPro')] + [Parameter(ParameterSetName = 'TeamMailboxIW')] + [Parameter(ParameterSetName = 'Shared')] + [Parameter(ParameterSetName = 'User')] + [bool] + ${ModerationEnabled}, + + [Parameter(Mandatory = $true, Position = 0, ValueFromPipelineByPropertyName = $true)] + [string] + ${Name}, + + [Parameter(ParameterSetName = 'LinkedRoomMailbox')] + [Parameter(ParameterSetName = 'Room')] + [string] + ${Office}, + + [System.Object] + ${OrganizationalUnit}, + + [Parameter(ParameterSetName = 'MicrosoftOnlineServicesID')] + [Parameter(ParameterSetName = 'RemoteArchive', Mandatory = $true)] + [Parameter(ParameterSetName = 'InactiveMailbox')] + [Parameter(ParameterSetName = 'RemovedMailbox')] + [Parameter(ParameterSetName = 'DisabledUser')] + [Parameter(ParameterSetName = 'Discovery')] + [Parameter(ParameterSetName = 'Migration')] + [Parameter(ParameterSetName = 'Arbitration')] + [Parameter(ParameterSetName = 'WindowsLiveID', Mandatory = $true)] + [Parameter(ParameterSetName = 'TeamMailboxITPro')] + [Parameter(ParameterSetName = 'TeamMailboxIW')] + [Parameter(ParameterSetName = 'Shared')] + [Parameter(ParameterSetName = 'Scheduling')] + [Parameter(ParameterSetName = 'Equipment')] + [Parameter(ParameterSetName = 'Room')] + [Parameter(ParameterSetName = 'LinkedRoomMailbox')] + [Parameter(ParameterSetName = 'LinkedWithSyncMailbox')] + [Parameter(ParameterSetName = 'Linked')] + [Parameter(ParameterSetName = 'User', Mandatory = $true)] + [securestring] + ${Password}, + + [Parameter(ParameterSetName = 'LinkedRoomMailbox')] + [Parameter(ParameterSetName = 'Room')] + [string] + ${Phone}, + + [System.Object] + ${PrimarySmtpAddress}, + + [Parameter(ParameterSetName = 'PublicFolder', Mandatory = $true)] + [switch] + ${PublicFolder}, + + [bool] + ${RemotePowerShellEnabled}, + + [Parameter(ParameterSetName = 'User', ValueFromPipeline = $true)] + [Parameter(ParameterSetName = 'ImportLiveId', ValueFromPipeline = $true)] + [Parameter(ParameterSetName = 'RemoteArchive', ValueFromPipeline = $true)] + [Parameter(ParameterSetName = 'RemovedMailbox', Mandatory = $true, ValueFromPipeline = $true)] + [Parameter(ParameterSetName = 'WindowsLiveCustomDomains', ValueFromPipeline = $true)] + [Parameter(ParameterSetName = 'MicrosoftOnlineServicesFederatedUser', ValueFromPipeline = $true)] + [Parameter(ParameterSetName = 'FederatedUser', ValueFromPipeline = $true)] + [Parameter(ParameterSetName = 'MicrosoftOnlineServicesID', ValueFromPipeline = $true)] + [Parameter(ParameterSetName = 'WindowsLiveID', ValueFromPipeline = $true)] + [System.Object] + ${RemovedMailbox}, + + [bool] + ${ResetPasswordOnNextLogon}, + + [Parameter(ParameterSetName = 'LinkedRoomMailbox')] + [Parameter(ParameterSetName = 'Room')] + [System.Object] + ${ResourceCapacity}, + + [System.Object] + ${RoleAssignmentPolicy}, + + [Parameter(ParameterSetName = 'EnableRoomMailboxAccount', Mandatory = $true)] + [Parameter(ParameterSetName = 'Room', Mandatory = $true)] + [switch] + ${Room}, + + [Parameter(ParameterSetName = 'EnableRoomMailboxAccount')] + [securestring] + ${RoomMailboxPassword}, + + [Parameter(ParameterSetName = 'GroupMailbox')] + [Parameter(ParameterSetName = 'RemoteArchive')] + [Parameter(ParameterSetName = 'InactiveMailbox')] + [Parameter(ParameterSetName = 'RemovedMailbox')] + [Parameter(ParameterSetName = 'DisabledUser')] + [Parameter(ParameterSetName = 'ImportLiveId')] + [Parameter(ParameterSetName = 'WindowsLiveCustomDomains')] + [Parameter(ParameterSetName = 'MailboxPlan')] + [Parameter(ParameterSetName = 'MicrosoftOnlineServicesID')] + [Parameter(ParameterSetName = 'WindowsLiveID')] + [Parameter(ParameterSetName = 'Equipment')] + [Parameter(ParameterSetName = 'Room')] + [Parameter(ParameterSetName = 'LinkedRoomMailbox')] + [Parameter(ParameterSetName = 'LinkedWithSyncMailbox')] + [Parameter(ParameterSetName = 'Linked')] + [Parameter(ParameterSetName = 'TeamMailboxITPro')] + [Parameter(ParameterSetName = 'TeamMailboxIW')] + [Parameter(ParameterSetName = 'Shared')] + [Parameter(ParameterSetName = 'User')] + [System.Object] + ${SendModerationNotifications}, + + [Parameter(ParameterSetName = 'Shared', Mandatory = $true)] + [switch] + ${Shared}, + + [switch] + ${TargetAllMDBs}) + +begin +{ + try { + $outBuffer = $null + if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer)) { + $PSBoundParameters['OutBuffer'] = 1 + } + + $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('New-Mailbox', [System.Management.Automation.CommandTypes]::Function) + $scriptCmd = { & $wrappedCmd @PSBoundParameters } + + $steppablePipeline = $scriptCmd.GetSteppablePipeline() + $steppablePipeline.Begin($PSCmdlet) + } catch { + throw + } +} + +process +{ + try { + $steppablePipeline.Process($_) + } catch { + throw + } +} + +end +{ + try { + $steppablePipeline.End() + } catch { + throw + } +} + +clean +{ + if ($null -ne $steppablePipeline) { + $steppablePipeline.Clean() + } +} +<# + +.ForwardHelpTargetName New-Mailbox +.ForwardHelpCategory Function + +#> \ No newline at end of file From dff4b6af7e0acb7bf82862fcc790ef992a709d88 Mon Sep 17 00:00:00 2001 From: Adam Bertram Date: Thu, 23 May 2024 15:27:01 -0500 Subject: [PATCH 04/19] added Add-ExchangeOnlineMailboxPermission --- .../Add-ExchangeOnlineMailboxPermission.ps1 | 160 ++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 Microsoft Exchange Online/Add-ExchangeOnlineMailboxPermission.ps1 diff --git a/Microsoft Exchange Online/Add-ExchangeOnlineMailboxPermission.ps1 b/Microsoft Exchange Online/Add-ExchangeOnlineMailboxPermission.ps1 new file mode 100644 index 0000000..a1cf7ca --- /dev/null +++ b/Microsoft Exchange Online/Add-ExchangeOnlineMailboxPermission.ps1 @@ -0,0 +1,160 @@ +#requires -Modules @{ModuleName="ExchangeOnlineManagement";ModuleVersion="3.5.0"} + +<# +.SYNOPSIS + Adds permissions to a mailbox in an Exchange environment. + +.DESCRIPTION + This script is a wrapper around the `Add-MailboxPermission` cmdlet, providing a variety of parameter sets to handle different scenarios. + It supports adding access rights, setting mailbox owner, and handling inheritance types for mailbox permissions. + +.PARAMETER AccessRights + Specifies the access rights to assign to the user on the mailbox. This parameter is mandatory for the AccessRights parameter set. + +.PARAMETER AutoMapping + Specifies whether automapping is enabled. Used in conjunction with the AccessRights and Instance parameter sets. + +.PARAMETER Deny + Specifies whether to deny the specified access rights. Used in conjunction with the AccessRights and Instance parameter sets. + +.PARAMETER GroupMailbox + Specifies whether the mailbox is a group mailbox. Used in conjunction with the Owner, Instance, and AccessRights parameter sets. + +.PARAMETER Identity + Specifies the mailbox to which permissions are being added. This parameter is mandatory for multiple parameter sets. + +.PARAMETER IgnoreDefaultScope + Specifies whether to ignore the default scope and search in the entire forest. + +.PARAMETER InheritanceType + Specifies the type of inheritance for the permission. Used in conjunction with the AccessRights and Instance parameter sets. + +.PARAMETER Owner + Specifies the owner of the mailbox. This parameter is mandatory for the Owner parameter set. + +.PARAMETER User + Specifies the user to whom the permissions are being granted. This parameter is mandatory for the AccessRights parameter set. + +.NOTES + For more information on the `Add-MailboxPermission` cmdlet, visit: + https://learn.microsoft.com/en-us/powershell/module/exchange/add-mailboxpermission + +.EXAMPLE + PS> .\Add-ExchangeOnlineMailboxPermission.ps1 -Identity "UserMailbox" -User "AnotherUser" -AccessRights FullAccess + + This example grants the 'FullAccess' permission to 'AnotherUser' on 'UserMailbox'. + +.EXAMPLE + PS> .\Add-ExchangeOnlineMailboxPermission.ps1 -Identity "UserMailbox" -Owner "NewOwner" + + This example sets 'NewOwner' as the owner of 'UserMailbox'. + +.EXAMPLE + PS> .\Add-ExchangeOnlineMailboxPermission.ps1 -Identity "UserMailbox" -User "AnotherUser" -AccessRights FullAccess -Deny + + This example denies the 'FullAccess' permission to 'AnotherUser' on 'UserMailbox'. + +.EXAMPLE + PS> .\Add-ExchangeOnlineMailboxPermission.ps1 -Identity "UserMailbox" -ClearAutoMapping + + This example clears automapping for 'UserMailbox'. +#> + + +[CmdletBinding(DefaultParameterSetName='AccessRights', SupportsShouldProcess=$true, ConfirmImpact='Medium')] +param( + [Parameter(ParameterSetName='AccessRights', Mandatory=$true)] + [Parameter(ParameterSetName='Instance')] + [System.Object[]] + ${AccessRights}, + + [Parameter(ParameterSetName='AccessRights')] + [Parameter(ParameterSetName='Instance')] + [System.Object] + ${AutoMapping}, + + [Parameter(ParameterSetName='AccessRights')] + [Parameter(ParameterSetName='Instance')] + [switch] + ${Deny}, + + [Parameter(ParameterSetName='Owner')] + [Parameter(ParameterSetName='Instance')] + [Parameter(ParameterSetName='AccessRights')] + [switch] + ${GroupMailbox}, + + [Parameter(ParameterSetName='ClearAutoMapping', Mandatory=$true, Position=0, ValueFromPipelineByPropertyName=$true)] + [Parameter(ParameterSetName='ResetDefault', Mandatory=$true, Position=0, ValueFromPipelineByPropertyName=$true)] + [Parameter(ParameterSetName='Owner', Mandatory=$true, Position=0, ValueFromPipelineByPropertyName=$true)] + [Parameter(ParameterSetName='AccessRights', Mandatory=$true, Position=0, ValueFromPipelineByPropertyName=$true)] + [Parameter(ParameterSetName='Instance', Position=0)] + [System.Object] + ${Identity}, + + [switch] + ${IgnoreDefaultScope}, + + [Parameter(ParameterSetName='AccessRights')] + [Parameter(ParameterSetName='Instance')] + [System.DirectoryServices.ActiveDirectorySecurityInheritance] + ${InheritanceType}, + + [Parameter(ParameterSetName='Owner', Mandatory=$true)] + [System.Object] + ${Owner}, + + [Parameter(ParameterSetName='AccessRights', Mandatory=$true)] + [Parameter(ParameterSetName='Instance')] + [System.Object] + ${User}) + +begin +{ + try { + $outBuffer = $null + if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer)) + { + $PSBoundParameters['OutBuffer'] = 1 + } + + $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Add-MailboxPermission', [System.Management.Automation.CommandTypes]::Function) + $scriptCmd = {& $wrappedCmd @PSBoundParameters } + + $steppablePipeline = $scriptCmd.GetSteppablePipeline() + $steppablePipeline.Begin($PSCmdlet) + } catch { + throw + } +} + +process +{ + try { + $steppablePipeline.Process($_) + } catch { + throw + } +} + +end +{ + try { + $steppablePipeline.End() + } catch { + throw + } +} + +clean +{ + if ($null -ne $steppablePipeline) { + $steppablePipeline.Clean() + } +} +<# + +.ForwardHelpTargetName Add-MailboxPermission +.ForwardHelpCategory Function + +#> From e72cece5ae2aa0a9d33b8de21a26338db4a3a98a Mon Sep 17 00:00:00 2001 From: Adam Bertram Date: Thu, 23 May 2024 15:42:44 -0500 Subject: [PATCH 05/19] added Add-ExchangeOnlineMailboxAlias --- .../Add-ExchangeOnlineMailboxAlias.ps1 | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 Microsoft Exchange Online/Add-ExchangeOnlineMailboxAlias.ps1 diff --git a/Microsoft Exchange Online/Add-ExchangeOnlineMailboxAlias.ps1 b/Microsoft Exchange Online/Add-ExchangeOnlineMailboxAlias.ps1 new file mode 100644 index 0000000..d1e2fc7 --- /dev/null +++ b/Microsoft Exchange Online/Add-ExchangeOnlineMailboxAlias.ps1 @@ -0,0 +1,53 @@ +#requires -Modules @{ModuleName="ExchangeOnlineManagement";ModuleVersion="3.5.0"} + +<# +.SYNOPSIS + Adds a new alias to an existing mailbox in Exchange Online. + +.DESCRIPTION + This script retrieves the specified mailbox using the provided identity and adds a new alias email address. + The new alias is constructed using the provided alias and the domain from the mailbox's primary SMTP address. + If the mailbox cannot be found, an appropriate error message is displayed. + +.PARAMETER Identity + The identity of the mailbox (e.g., email address, user principal name, or alias) to which the new alias will be added. + This parameter is mandatory. + +.PARAMETER Alias + The alias to be added to the mailbox. This parameter is mandatory. + +.NOTES + This script requires the ExchangeOnlineManagement module version 3.5.0 or higher. + For more information on managing Exchange Online mailboxes, visit: + https://docs.microsoft.com/en-us/powershell/exchange/exchange-online/exchange-online-powershell + +.EXAMPLE + PS> .\Add-MailboxAlias.ps1 -Identity "user@example.com" -Alias "newalias" + + This example adds the alias "newalias@example.com" to the mailbox identified by "user@example.com". +#> + + +[CmdletBinding()] +param ( + [Parameter(Mandatory)] + [string]$Identity, + + [Parameter(Mandatory)] + [string]$Alias +) + +$errorActionPreference = "Stop" + +try { + $mbox = Get-Mailbox -Identity $Identity +} catch { + if ($_.Exception.Message -like "*because object '*' couldn't be found*") { + throw "Mailbox not found for user: $Identity" + } else { + throw $_ + } +} + +$newEmail = "$Alias@$(([mailaddress]$mbox.PrimarySmtpAddress).Host)" +Set-Mailbox -Identity $Identity -EmailAddresses @{Add = $newEmail } \ No newline at end of file From 7ee3478feb7d56d60b6742914a3a26f4dda0cb88 Mon Sep 17 00:00:00 2001 From: Adam Bertram Date: Mon, 3 Jun 2024 10:30:58 -0500 Subject: [PATCH 06/19] added get-sharepoinonlinegroupmember --- .../Get-SharePointOnlineGroupMember.ps1 | 139 ++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 Microsoft SharePoint Online/Get-SharePointOnlineGroupMember.ps1 diff --git a/Microsoft SharePoint Online/Get-SharePointOnlineGroupMember.ps1 b/Microsoft SharePoint Online/Get-SharePointOnlineGroupMember.ps1 new file mode 100644 index 0000000..67d9092 --- /dev/null +++ b/Microsoft SharePoint Online/Get-SharePointOnlineGroupMember.ps1 @@ -0,0 +1,139 @@ +#requires -Modules @{ModuleName="pnp.powershell";ModuleVersion="2.4.0"} + +<# +.SYNOPSIS + Connects to a SharePoint tenant and retrieves members of specified site groups. + +.DESCRIPTION + This script connects to a SharePoint tenant using either certificate or client secret authentication, then retrieves and lists + members of the specified site groups. The connection parameters are specified through mandatory parameters, and the script + ensures that proper authentication is used based on the provided parameters. + +.PARAMETER EntraIdTenantId + The GUID of the Entra ID (Azure AD) tenant. + +.PARAMETER SharePointTenant + The name of the SharePoint tenant. + +.PARAMETER PrivateKeyFilePath + The file path to the private key used for certificate authentication. This parameter is mandatory if using certificate + authentication. + +.PARAMETER PrivateKeyPassword + The password for the private key file used for certificate authentication. This parameter is mandatory if using certificate + authentication. + +.PARAMETER ClientId + The client ID of the Azure AD app registration. + +.PARAMETER ClientSecret + The client secret used for client secret authentication. This parameter is mandatory if using client secret authentication. + +.PARAMETER GroupName + The name of the SharePoint group to retrieve members from. Optional parameter. + +.PARAMETER SiteUrl + The URL of the SharePoint site to retrieve groups from. Optional parameter. + +.NOTES + For more information on using PnP PowerShell, visit: https://pnp.github.io/powershell/ + +.EXAMPLE + PS> .\Get-SharePointGroupMembers.ps1 -EntraIdTenantId 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' -SharePointTenant 'contoso' -ClientId 'xxxx-xxxx-xxxx-xxxx' -ClientSecret (ConvertTo-SecureString 'your-secret' -AsPlainText -Force) + + + Connects to the SharePoint tenant 'contoso' using client secret authentication and lists members of all site groups. + +.EXAMPLE + $sharePointOnlineRequiredResourceAccess = @( + @{ + ResourceAppId = "00000003-0000-0ff1-ce00-000000000000" ## Sharepoint Online resource app id + ResourceAccess = @( + @{ + Id = "fbcd29d2-fcca-4405-aded-518d457caae4" # Permission ID: Sites.ReadWrite.All + Type = "Role" + } + ) + } + ) + $app = New-AzADApplication -DisplayName SharePointOnlineSiteAuth -RequiredResourceAccess $sharePointOnlineRequiredResourceAccess + PS> Connect-AzAccount + PS> $tenantId = (Get-AzContext).Tenant.Id + PS> $cert = New-SelfSignedCertificate -DnsName PowerShellAuth -CertStoreLocation cert:\CurrentUser\My + PS> $pwd = ConvertTo-SecureString -String "P@ss0word!" -Force -AsPlainText + PS> Export-PfxCertificate -Cert $cert -FilePath .\cert.pfx -Password $pwd + PS> Export-Certificate -Cert $cert -FilePath .\cert.cer + + ## remove the cert from the cert store + PS> $cert | Remove-Item + + PS> $PfxCertificate = Get-PfxCertificate -FilePath ~/cert.pfx -Password (ConvertTo-SecureString -String 'P@ss0word!' -AsPlainText -Force) + + PS> .\Get-SharePointGroupMembers.ps1 -EntraIdTenantId $tenantId -SharePointTenant 'contoso' -ClientId $app.AppId -PrivateKeyFilePath '.\cert.pfx -PrivateKeyPassword $pwd -GroupName 'Site Members' + + + Connects to the SharePoint tenant 'contoso' using certificate authentication and lists members of the 'Site Members' group. +#> + +[CmdletBinding()] +param( + [Parameter(Mandatory)] + [guid]$EntraIdTenantId, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$SharePointTenant, + + [Parameter(Mandatory, ParameterSetName = 'CertificateAuth')] + [ValidateNotNullOrEmpty()] + [string]$PrivateKeyFilePath, + + [Parameter(Mandatory, ParameterSetName = 'CertificateAuth')] + [ValidateNotNullOrEmpty()] + [securestring]$PrivateKeyPassword, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$ClientId, + + [Parameter(Mandatory, ParameterSetName = 'ClientSecretAuth')] + [ValidateNotNullOrEmpty()] + [securestring]$ClientSecret, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$GroupName, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [uri]$SiteUrl +) + +$ErrorActionPreference = 'Stop' + +$connectParams = @{ + ClientId = $ClientId + Tenant = $EntraIdTenantId + Url = "https://$SharePointTenant.sharepoint.com" +} + +if ($PSCmdlet.ParameterSetName -eq 'CertificateAuth') { + $connectParams.CertificatePath = $PrivateKeyFilePath + $connectParams.CertificatePassword = $PrivateKeyPassword +} else { + $connectParams.ClientSecret = $ClientSecret +} + +Connect-PnPOnline @connectParams + +$getSiteGroupParams = @{} +if ($PSBoundParameters.ContainsKey('GroupName')) { + $getSiteGroupParams.Group = $GroupName +} +if ($PSBoundParameters.ContainsKey('SiteUrl')) { + $getSiteGroupParams.Site = $SiteUrl.ToString() +} + +Get-PnPSiteGroup @getSiteGroupParams | ForEach-Object { + Get-PnPGroupMember -Group $_.Title +} \ No newline at end of file From 2cfb938b1b342a721ce24e2290fc202bf85bf799 Mon Sep 17 00:00:00 2001 From: Adam Bertram Date: Mon, 3 Jun 2024 10:50:42 -0500 Subject: [PATCH 07/19] output update to get-sharepointonlinegroupmember --- .../Get-SharePointOnlineGroupMember.ps1 | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Microsoft SharePoint Online/Get-SharePointOnlineGroupMember.ps1 b/Microsoft SharePoint Online/Get-SharePointOnlineGroupMember.ps1 index 67d9092..817c8bd 100644 --- a/Microsoft SharePoint Online/Get-SharePointOnlineGroupMember.ps1 +++ b/Microsoft SharePoint Online/Get-SharePointOnlineGroupMember.ps1 @@ -134,6 +134,12 @@ if ($PSBoundParameters.ContainsKey('SiteUrl')) { $getSiteGroupParams.Site = $SiteUrl.ToString() } -Get-PnPSiteGroup @getSiteGroupParams | ForEach-Object { - Get-PnPGroupMember -Group $_.Title +Get-PnPSiteGroup @getSiteGroupParams -PipelineVariable 'group' | ForEach-Object { + Get-PnPGroupMember -Group $group.Title | ForEach-Object { + [pscustomobject]@{ + GroupName = $group.Title + LoginName = $_.LoginName + Title = $_.Title + } + } } \ No newline at end of file From ea5485afaf9f1f5bd5b2ffbc8778934a5ed599b3 Mon Sep 17 00:00:00 2001 From: Adam Bertram Date: Mon, 3 Jun 2024 11:00:51 -0500 Subject: [PATCH 08/19] added add-sharepointonlinegroupmember --- .../Add-SharePointOnlineGroupMember.ps1 | 125 ++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 Microsoft SharePoint Online/Add-SharePointOnlineGroupMember.ps1 diff --git a/Microsoft SharePoint Online/Add-SharePointOnlineGroupMember.ps1 b/Microsoft SharePoint Online/Add-SharePointOnlineGroupMember.ps1 new file mode 100644 index 0000000..72af195 --- /dev/null +++ b/Microsoft SharePoint Online/Add-SharePointOnlineGroupMember.ps1 @@ -0,0 +1,125 @@ +#requires -Modules @{ModuleName="pnp.powershell";ModuleVersion="2.4.0"} + +<# +.SYNOPSIS + Connects to a SharePoint tenant and adds a specified user to a specified group. + +.DESCRIPTION + This script connects to a SharePoint tenant using either certificate-based or client secret-based authentication. + It verifies the existence of the specified group and user within the SharePoint tenant. If both the group and + user are found, it adds the user to the group. + +.PARAMETER EntraIdTenantId + The GUID representing the Entra ID (Azure Active Directory) Tenant ID. + +.PARAMETER SharePointTenant + The name of the SharePoint tenant. + +.PARAMETER PrivateKeyFilePath + The file path to the private key used for certificate-based authentication. + +.PARAMETER PrivateKeyPassword + The password for the private key used in certificate-based authentication. + +.PARAMETER ClientId + The client ID for the Azure AD application. + +.PARAMETER ClientSecret + The client secret for client secret-based authentication. + +.PARAMETER GroupName + The name of the SharePoint group to which the user will be added. + +.PARAMETER LoginName + The login name of the user to be added to the group. + +.PARAMETER SiteUrl + The URL of the SharePoint site. This parameter is optional. + +.NOTES + To run this script, you need the PnP PowerShell module installed with version 2.4.0 or higher. + For more information on PnP PowerShell, visit: https://pnp.github.io/powershell/ + +.EXAMPLE + PS> .\Add-SharePointOnlineGroupMember.ps1 -EntraIdTenantId "00000000-0000-0000-0000-000000000000" -SharePointTenant "yourtenant" ` + -PrivateKeyFilePath "C:\keys\privatekey.pfx" -PrivateKeyPassword (ConvertTo-SecureString "password" -AsPlainText -Force) ` + -ClientId "00000000-0000-0000-0000-000000000000" -GroupName "TeamSite Members" -LoginName "user@domain.com" + Connects to the specified SharePoint tenant using certificate-based authentication and adds the user to the specified group. + +.EXAMPLE + PS> .\Add-SharePointOnlineGroupMember.ps1 -EntraIdTenantId "00000000-0000-0000-0000-000000000000" -SharePointTenant "yourtenant" ` + -ClientId "00000000-0000-0000-0000-000000000000" -ClientSecret (ConvertTo-SecureString "secret" -AsPlainText -Force) ` + -GroupName "TeamSite Members" -LoginName "user@domain.com" + Connects to the specified SharePoint tenant using client secret-based authentication and adds the user to the specified group. +#> + +[CmdletBinding()] +param( + [Parameter(Mandatory)] + [guid]$EntraIdTenantId, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$SharePointTenant, + + [Parameter(Mandatory, ParameterSetName = 'CertificateAuth')] + [ValidateNotNullOrEmpty()] + [string]$PrivateKeyFilePath, + + [Parameter(Mandatory, ParameterSetName = 'CertificateAuth')] + [ValidateNotNullOrEmpty()] + [securestring]$PrivateKeyPassword, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$ClientId, + + [Parameter(Mandatory, ParameterSetName = 'ClientSecretAuth')] + [ValidateNotNullOrEmpty()] + [securestring]$ClientSecret, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$GroupName, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$LoginName, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [uri]$SiteUrl +) + +$ErrorActionPreference = 'Stop' + +$connectParams = @{ + ClientId = $ClientId + Tenant = $EntraIdTenantId + Url = "https://$SharePointTenant.sharepoint.com" +} + +if ($PSCmdlet.ParameterSetName -eq 'CertificateAuth') { + $connectParams.CertificatePath = $PrivateKeyFilePath + $connectParams.CertificatePassword = $PrivateKeyPassword +} else { + $connectParams.ClientSecret = $ClientSecret +} + +Connect-PnPOnline @connectParams + +try { + $null = Get-PnPSiteGroup -Group $GroupName +} catch { + if ($_.Exception.Message -like '*Group not found*') { + throw "The group [$GroupName] was not found" + } else { + throw $_ + } +} + +if (-not (Get-PnpUser -Identity $LoginName)) { + throw "User [$LoginName] was not found" +} + +Add-PnPGroupMember -LoginName $LoginName -Group $GroupName \ No newline at end of file From 11287e434d4393f29618ebeba354f1dc850f1cb9 Mon Sep 17 00:00:00 2001 From: Adam Bertram Date: Mon, 3 Jun 2024 11:15:24 -0500 Subject: [PATCH 09/19] added get-sharepointonlinesitestoragesize --- .../Get-SharePointOnlineSiteStorageSize.ps1 | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 Microsoft SharePoint Online/Get-SharePointOnlineSiteStorageSize.ps1 diff --git a/Microsoft SharePoint Online/Get-SharePointOnlineSiteStorageSize.ps1 b/Microsoft SharePoint Online/Get-SharePointOnlineSiteStorageSize.ps1 new file mode 100644 index 0000000..a8deaa5 --- /dev/null +++ b/Microsoft SharePoint Online/Get-SharePointOnlineSiteStorageSize.ps1 @@ -0,0 +1,60 @@ +#requires -Modules @{ModuleName="pnp.powershell";ModuleVersion="2.4.0"} + +[CmdletBinding()] +param( + [Parameter(Mandatory)] + [guid]$EntraIdTenantId, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$SharePointTenant, + + [Parameter(Mandatory, ParameterSetName = 'CertificateAuth')] + [ValidateNotNullOrEmpty()] + [string]$PrivateKeyFilePath, + + [Parameter(Mandatory, ParameterSetName = 'CertificateAuth')] + [ValidateNotNullOrEmpty()] + [securestring]$PrivateKeyPassword, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$ClientId, + + [Parameter(Mandatory, ParameterSetName = 'ClientSecretAuth')] + [ValidateNotNullOrEmpty()] + [securestring]$ClientSecret, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [uri]$SiteUrl +) + +$ErrorActionPreference = 'Stop' + +$connectParams = @{ + ClientId = $ClientId + Tenant = $EntraIdTenantId + Url = "https://$SharePointTenant.sharepoint.com" +} + +if ($PSCmdlet.ParameterSetName -eq 'CertificateAuth') { + $connectParams.CertificatePath = $PrivateKeyFilePath + $connectParams.CertificatePassword = $PrivateKeyPassword +} else { + $connectParams.ClientSecret = $ClientSecret +} + +Connect-PnPOnline @connectParams + +$getPnPSiteParams = @{} +if ($PSBoundParameters.ContainsKey('SiteUrl')) { + $getPnPSiteParams.Identity = $SiteUrl.ToString() +} + +Get-PnPTenantSite @getPnPSiteParams | ForEach-Object { + [pscustomobject]@{ + SiteUrl = $_.Url + SizeMB = $_.StorageUsageCurrent + } +} \ No newline at end of file From 226aecf4fd23f471134e65c2bc39c78b1dff649b Mon Sep 17 00:00:00 2001 From: Adam Bertram Date: Mon, 3 Jun 2024 11:25:41 -0500 Subject: [PATCH 10/19] added help to get-sharepointonlinesitestoragesize --- .../Get-SharePointOnlineSiteStorageSize.ps1 | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/Microsoft SharePoint Online/Get-SharePointOnlineSiteStorageSize.ps1 b/Microsoft SharePoint Online/Get-SharePointOnlineSiteStorageSize.ps1 index a8deaa5..95176da 100644 --- a/Microsoft SharePoint Online/Get-SharePointOnlineSiteStorageSize.ps1 +++ b/Microsoft SharePoint Online/Get-SharePointOnlineSiteStorageSize.ps1 @@ -1,5 +1,56 @@ #requires -Modules @{ModuleName="pnp.powershell";ModuleVersion="2.4.0"} +<# +.SYNOPSIS + Connects to a SharePoint tenant and retrieves a list of site collections with their URLs and storage usage. + +.DESCRIPTION + This script connects to a SharePoint tenant using either certificate-based or client secret-based authentication. + It retrieves and lists all site collections within the SharePoint tenant, displaying each site's URL and current storage usage in megabytes. + +.PARAMETER EntraIdTenantId + The GUID representing the Entra ID (Azure Active Directory) Tenant ID. + +.PARAMETER SharePointTenant + The name of the SharePoint tenant. + +.PARAMETER PrivateKeyFilePath + The file path to the private key used for certificate-based authentication. + +.PARAMETER PrivateKeyPassword + The password for the private key used in certificate-based authentication. + +.PARAMETER ClientId + The client ID for the Azure AD application. + +.PARAMETER ClientSecret + The client secret for client secret-based authentication. + +.PARAMETER SiteUrl + The URL of a specific SharePoint site. This parameter is optional and, if provided, the script will retrieve information for this site only. + +.NOTES + To run this script, you need the PnP PowerShell module installed with version 2.4.0 or higher. + For more information on PnP PowerShell, visit: https://pnp.github.io/powershell/ + +.EXAMPLE + PS> .\Get-SharePointOnlineSiteStorageSize -EntraIdTenantId "00000000-0000-0000-0000-000000000000" -SharePointTenant "yourtenant" ` + -PrivateKeyFilePath "C:\keys\privatekey.pfx" -PrivateKeyPassword (ConvertTo-SecureString "password" -AsPlainText -Force) ` + -ClientId "00000000-0000-0000-0000-000000000000" + Connects to the specified SharePoint tenant using certificate-based authentication and retrieves all site collections with their URLs and storage usage. + +.EXAMPLE + PS> .\Get-SharePointOnlineSiteStorageSize -EntraIdTenantId "00000000-0000-0000-0000-000000000000" -SharePointTenant "yourtenant" ` + -ClientId "00000000-0000-0000-0000-000000000000" -ClientSecret (ConvertTo-SecureString "secret" -AsPlainText -Force) + Connects to the specified SharePoint tenant using client secret-based authentication and retrieves all site collections with their URLs and storage usage. + +.EXAMPLE + PS> .\Get-SharePointOnlineSiteStorageSize -EntraIdTenantId "00000000-0000-0000-0000-000000000000" -SharePointTenant "yourtenant" ` + -ClientId "00000000-0000-0000-0000-000000000000" -ClientSecret (ConvertTo-SecureString "secret" -AsPlainText -Force) ` + -SiteUrl "https://yourtenant.sharepoint.com/sites/specificsite" + Connects to the specified SharePoint tenant using client secret-based authentication and retrieves information for the specified site only. +#> + [CmdletBinding()] param( [Parameter(Mandatory)] From d8803fa4fe7269bb1eda199a47890a3b581d7711 Mon Sep 17 00:00:00 2001 From: Adam Bertram Date: Mon, 3 Jun 2024 12:35:47 -0500 Subject: [PATCH 11/19] added Invoke-SharePointOnlineFileDownload --- .../Invoke-SharePointOnlineFileDownload.ps1 | 128 ++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 Microsoft SharePoint Online/Invoke-SharePointOnlineFileDownload.ps1 diff --git a/Microsoft SharePoint Online/Invoke-SharePointOnlineFileDownload.ps1 b/Microsoft SharePoint Online/Invoke-SharePointOnlineFileDownload.ps1 new file mode 100644 index 0000000..fe28cb3 --- /dev/null +++ b/Microsoft SharePoint Online/Invoke-SharePointOnlineFileDownload.ps1 @@ -0,0 +1,128 @@ +#requires -Modules @{ModuleName="pnp.powershell";ModuleVersion="2.4.0"} + +<# +.SYNOPSIS + Connects to a SharePoint tenant and downloads a specified file from the site to a local folder. + +.DESCRIPTION + This script connects to a SharePoint tenant using either certificate-based or client secret-based authentication. + It downloads a file from the specified SharePoint path to a local folder. The script ensures that the destination + folder exists and supports overwriting the file if the -Force switch is specified. + +.PARAMETER EntraIdTenantId + The GUID representing the Entra ID (Azure Active Directory) Tenant ID. + +.PARAMETER SharePointTenant + The name of the SharePoint tenant. + +.PARAMETER PrivateKeyFilePath + The file path to the private key used for certificate-based authentication. + +.PARAMETER PrivateKeyPassword + The password for the private key used in certificate-based authentication. + +.PARAMETER ClientId + The client ID for the Azure AD application. + +.PARAMETER ClientSecret + The client secret for client secret-based authentication. + +.PARAMETER SourceFilePath + The URI path of the source file in the SharePoint site. + +.PARAMETER DestinationFolderPath + The local path to the folder where the file will be downloaded. The folder must exist. + +.PARAMETER Force + If specified, forces the download by overwriting the file if it already exists in the destination folder. + +.NOTES + To run this script, you need the PnP PowerShell module installed with version 2.4.0 or higher. + For more information on PnP PowerShell, visit: https://pnp.github.io/powershell/ + +.EXAMPLE + PS> .\Invoke-SharePointOnlineFileDownload.ps1 -EntraIdTenantId "00000000-0000-0000-0000-000000000000" -SharePointTenant "yourtenant" ` + -PrivateKeyFilePath "C:\keys\privatekey.pfx" -PrivateKeyPassword (ConvertTo-SecureString "password" -AsPlainText -Force) ` + -ClientId "00000000-0000-0000-0000-000000000000" -SourceFilePath "/sites/sitecollection/documents/file.txt" ` + -DestinationFolderPath "C:\Downloads" + Connects to the specified SharePoint tenant using certificate-based authentication and downloads the specified file to the local folder. + +.EXAMPLE + PS> .\Invoke-SharePointOnlineFileDownload -EntraIdTenantId "00000000-0000-0000-0000-000000000000" -SharePointTenant "yourtenant" ` + -ClientId "00000000-0000-0000-0000-000000000000" -ClientSecret (ConvertTo-SecureString "secret" -AsPlainText -Force) ` + -SourceFilePath "/sites/sitecollection/documents/file.txt" -DestinationFolderPath "C:\Downloads" -Force + Connects to the specified SharePoint tenant using client secret-based authentication and downloads the specified file to the local folder, + overwriting the file if it already exists. +#> + +[CmdletBinding()] +param( + [Parameter(Mandatory)] + [guid]$EntraIdTenantId, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$SharePointTenant, + + [Parameter(Mandatory, ParameterSetName = 'CertificateAuth')] + [ValidateNotNullOrEmpty()] + [string]$PrivateKeyFilePath, + + [Parameter(Mandatory, ParameterSetName = 'CertificateAuth')] + [ValidateNotNullOrEmpty()] + [securestring]$PrivateKeyPassword, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$ClientId, + + [Parameter(Mandatory, ParameterSetName = 'ClientSecretAuth')] + [ValidateNotNullOrEmpty()] + [securestring]$ClientSecret, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [uri]$SourceFilePath, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [ValidateScript( + { Test-Path -Path $_ -PathType Container }, + ErrorMessage = 'The folder [{0}] does not exist.' + )] + [string]$DestinationFolderPath, + + [Parameter()] + [switch]$Force +) + +$ErrorActionPreference = 'Stop' + +$tenantUrl = "https://$SharePointTenant.sharepoint.com" + +$connectParams = @{ + ClientId = $ClientId + Tenant = $EntraIdTenantId + Url = $tenantUrl +} + +if ($PSCmdlet.ParameterSetName -eq 'CertificateAuth') { + $connectParams.CertificatePath = $PrivateKeyFilePath + $connectParams.CertificatePassword = $PrivateKeyPassword +} else { + $connectParams.ClientSecret = $ClientSecret +} + +Connect-PnPOnline @connectParams + +$sourceFileUrl = '{0}/{1}' -f $tenantUrl, $SourceFilePath +$encodedSourceFileUrl = [uri]::EscapeUriString($sourceFileUrl) + +$pnpFileParams = @{ + Url = $encodedSourceFileUrl + Path = $DestinationFolderPath + FileName = ($SourceFilePath | Split-Path -Leaf) + AsFile = $true +} +$pnpFileParams.Force = $Force.IsPresent ? $true : $false +Get-PnPFile @pnpFileParams \ No newline at end of file From 0082a3ab29a9f9a68ca91b15c73835442d1997d7 Mon Sep 17 00:00:00 2001 From: Adam Bertram Date: Mon, 3 Jun 2024 12:46:39 -0500 Subject: [PATCH 12/19] added Invoke-SharePointOnlineFileUpload --- .../Invoke-SharePointOnlineFileUpload.ps1 | 118 ++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 Microsoft SharePoint Online/Invoke-SharePointOnlineFileUpload.ps1 diff --git a/Microsoft SharePoint Online/Invoke-SharePointOnlineFileUpload.ps1 b/Microsoft SharePoint Online/Invoke-SharePointOnlineFileUpload.ps1 new file mode 100644 index 0000000..abf9957 --- /dev/null +++ b/Microsoft SharePoint Online/Invoke-SharePointOnlineFileUpload.ps1 @@ -0,0 +1,118 @@ +#requires -Modules @{ModuleName="pnp.powershell";ModuleVersion="2.4.0"} + +<# +.SYNOPSIS + Connects to a SharePoint Online site and uploads a file to a specified folder. + +.DESCRIPTION + This script connects to a SharePoint Online site using either certificate authentication or client secret authentication. + After establishing the connection, it uploads a specified file to a destination folder within the SharePoint site. + +.PARAMETER EntraIdTenantId + The GUID of the Azure AD tenant ID. + +.PARAMETER SharePointTenant + The SharePoint Online tenant name. + +.PARAMETER PrivateKeyFilePath + The file path of the private key used for certificate authentication. + +.PARAMETER PrivateKeyPassword + The password for the private key used for certificate authentication. + +.PARAMETER ClientId + The Client ID of the Azure AD application. + +.PARAMETER ClientSecret + The client secret of the Azure AD application used for client secret authentication. + +.PARAMETER SourceFilePath + The full path of the source file to be uploaded. + +.PARAMETER DestinationFolderPath + The destination folder path within the SharePoint Online site where the file will be uploaded. + +.NOTES + Requires the 'pnp.powershell' module version 2.4.0 or higher. + For more information on PnP PowerShell, visit: https://pnp.github.io/powershell/ + +.EXAMPLE + PS> .\Invoke-SharePointOnlineFileUpload -EntraIdTenantId '00000000-0000-0000-0000-000000000000' -SharePointTenant 'contoso' -PrivateKeyFilePath + 'C:\path\to\privatekey.pfx' -PrivateKeyPassword (ConvertTo-SecureString -String 'password' -AsPlainText -Force) -ClientId + '00000000-0000-0000-0000-000000000000' -SourceFilePath 'C:\path\to\sourcefile.txt' -DestinationFolderPath 'Shared Documents' + + This example connects to the SharePoint Online site 'https://contoso.sharepoint.com' using certificate authentication + and uploads the file 'C:\path\to\sourcefile.txt' to the 'Shared Documents' folder. + +.EXAMPLE + PS> .\Invoke-SharePointOnlineFileUpload -EntraIdTenantId '00000000-0000-0000-0000-000000000000' -SharePointTenant 'contoso' -ClientId + '00000000-0000-0000-0000-000000000000' -ClientSecret (ConvertTo-SecureString -String 'clientsecret' -AsPlainText -Force) -SourceFilePath + 'C:\path\to\sourcefile.txt' -DestinationFolderPath 'Shared Documents' + + This example connects to the SharePoint Online site 'https://contoso.sharepoint.com' using client secret authentication + and uploads the file 'C:\path\to\sourcefile.txt' to the 'Shared Documents' folder. +#> + + +[CmdletBinding()] +param( + [Parameter(Mandatory)] + [guid]$EntraIdTenantId, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$SharePointTenant, + + [Parameter(Mandatory, ParameterSetName = 'CertificateAuth')] + [ValidateNotNullOrEmpty()] + [string]$PrivateKeyFilePath, + + [Parameter(Mandatory, ParameterSetName = 'CertificateAuth')] + [ValidateNotNullOrEmpty()] + [securestring]$PrivateKeyPassword, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$ClientId, + + [Parameter(Mandatory, ParameterSetName = 'ClientSecretAuth')] + [ValidateNotNullOrEmpty()] + [securestring]$ClientSecret, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [ValidateScript( + { Test-Path -Path $_ -PathType Leaf }, + ErrorMessage = 'The file [{0}] does not exist.' + )] + [uri]$SourceFilePath, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$DestinationFolderPath +) + +$ErrorActionPreference = 'Stop' + +$tenantUrl = "https://$SharePointTenant.sharepoint.com" + +$connectParams = @{ + ClientId = $ClientId + Tenant = $EntraIdTenantId + Url = $tenantUrl +} + +if ($PSCmdlet.ParameterSetName -eq 'CertificateAuth') { + $connectParams.CertificatePath = $PrivateKeyFilePath + $connectParams.CertificatePassword = $PrivateKeyPassword +} else { + $connectParams.ClientSecret = $ClientSecret +} + +Connect-PnPOnline @connectParams + +$pnpFileParams = @{ + Path = $SourceFilePath + Folder = $DestinationFolderPath +} +Add-PnPFile @pnpFileParams From 1a3cdaff8f3b0e4fc6746b3726eca9374164070f Mon Sep 17 00:00:00 2001 From: Adam Bertram Date: Mon, 3 Jun 2024 13:39:20 -0500 Subject: [PATCH 13/19] added install-windowsupdate proxy command --- Windows Update/Install-WindowsUpdate.ps1 | 371 +++++++++++++++++++++++ 1 file changed, 371 insertions(+) create mode 100644 Windows Update/Install-WindowsUpdate.ps1 diff --git a/Windows Update/Install-WindowsUpdate.ps1 b/Windows Update/Install-WindowsUpdate.ps1 new file mode 100644 index 0000000..a52fa1f --- /dev/null +++ b/Windows Update/Install-WindowsUpdate.ps1 @@ -0,0 +1,371 @@ +#requires -modules @{ModuleName='PSWindowsUpdate'; ModuleVersion='2.2.1.4'} + +<# +.SYNOPSIS + This script facilitates the installation, downloading, and management of Windows updates on one or multiple computers. + +.DESCRIPTION + The script utilizes the PSWindowsUpdate module to manage Windows updates. It supports various parameters for filtering + and specifying the updates to be managed, including scheduling options, criteria for selection, and actions to be taken. + The script also supports reporting and history features, allowing for detailed control and monitoring of update activities. + +.PARAMETER ComputerName + The name of the computer(s) on which to manage Windows updates. + +.PARAMETER SendReport + Sends a report of the update status. + +.PARAMETER PSWUSettings + A hashtable of settings for PSWindowsUpdate module. + +.PARAMETER SendHistory + Sends the update history report. + +.PARAMETER ScheduleJob + Schedules the update job to run at a specified date and time. + +.PARAMETER AcceptAll + Automatically accepts all updates. + +.PARAMETER RecurseCycle + Specifies the number of recursive cycles for checking updates. + +.PARAMETER Hide + Hides the specified updates. + +.PARAMETER Download + Downloads the specified updates. + +.PARAMETER ForceDownload + Forces the download of updates, ignoring any preconditions. + +.PARAMETER Install + Installs the specified updates. + +.PARAMETER ForceInstall + Forces the installation of updates, ignoring any preconditions. + +.PARAMETER AutoReboot + Automatically reboots the computer after installing updates. + +.PARAMETER IgnoreReboot + Ignores reboot requests after installing updates. + +.PARAMETER ScheduleReboot + Schedules a reboot at a specified date and time after updates are installed. + +.PARAMETER ServiceID + Specifies the service ID for the updates (parameter set: 'ServiceID'). + +.PARAMETER WindowsUpdate + Specifies to use Windows Update (parameter set: 'WindowsUpdate'). + +.PARAMETER MicrosoftUpdate + Specifies to use Microsoft Update (parameter set: 'MicrosoftUpdate'). + +.PARAMETER Criteria + Specifies criteria for selecting updates. + +.PARAMETER UpdateType + Specifies the type of updates to manage (Driver or Software). + +.PARAMETER DeploymentAction + Specifies the deployment action (Installation or Uninstallation). + +.PARAMETER IsAssigned + Filters updates that are assigned. + +.PARAMETER IsPresent + Filters updates that are present. + +.PARAMETER BrowseOnly + Browses for updates without installing them. + +.PARAMETER AutoSelectOnWebSites + Automatically selects updates on websites. + +.PARAMETER UpdateID + Specifies the IDs of updates to include. + +.PARAMETER NotUpdateID + Specifies the IDs of updates to exclude. + +.PARAMETER RevisionNumber + Specifies the revision number of updates. + +.PARAMETER CategoryIDs + Specifies the category IDs of updates. + +.PARAMETER IsInstalled + Filters updates that are installed. + +.PARAMETER IsHidden + Filters updates that are hidden. + +.PARAMETER WithHidden + Includes hidden updates. + +.PARAMETER ShowPreSearchCriteria + Shows the pre-search criteria. + +.PARAMETER RootCategories + Specifies the root categories of updates (e.g., Critical Updates, Security Updates, etc.). + +.PARAMETER Category + Specifies categories of updates to include. + +.PARAMETER KBArticleID + Specifies the KB article IDs of updates. + +.PARAMETER Title + Specifies the title of updates. + +.PARAMETER Severity + Specifies the severity of updates (e.g., Critical, Important, etc.). + +.PARAMETER NotCategory + Specifies categories of updates to exclude. + +.PARAMETER NotKBArticleID + Specifies the KB article IDs of updates to exclude. + +.PARAMETER NotTitle + Specifies the title of updates to exclude. + +.PARAMETER NotSeverity + Specifies the severity of updates to exclude. + +.PARAMETER IgnoreUserInput + Aliased as 'Silent', ignores user input during the update process. + +.PARAMETER IgnoreRebootRequired + Ignores reboot requirements after updates are installed. + +.PARAMETER AutoSelectOnly + Automatically selects updates to be installed. + +.PARAMETER MaxSize + Specifies the maximum size of updates to include. + +.PARAMETER MinSize + Specifies the minimum size of updates to include. + +.PARAMETER Debuger + Enables debugging for the update process. + +.NOTES + Requires the 'PSWindowsUpdate' module version 2.2.1.4 or higher. + For more information on PSWindowsUpdate, visit: https://github.com/PSWindowsUpdate/PSWindowsUpdate + +.EXAMPLE + PS> .\Install-WindowsUpdate.ps1 -ComputerName 'Server01' -Install -AutoReboot + + This example installs updates on 'Server01' and automatically reboots the computer after installation. + +.EXAMPLE + PS> .\Install-WindowsUpdate.ps1 -ComputerName 'Server01','Server02' -Download -SendReport + + This example downloads updates on 'Server01' and 'Server02' and sends a report of the update status. +#> + + +[CmdletBinding(DefaultParameterSetName = 'Default', SupportsShouldProcess = $true, ConfirmImpact = 'High')] +param( + [Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] + [string[]] + ${ComputerName}, + + [switch] + ${SendReport}, + + [hashtable] + ${PSWUSettings}, + + [switch] + ${SendHistory}, + + [datetime] + ${ScheduleJob}, + + [switch] + ${AcceptAll}, + + [int] + ${RecurseCycle}, + + [switch] + ${Hide}, + + [switch] + ${Download}, + + [switch] + ${ForceDownload}, + + [switch] + ${Install}, + + [switch] + ${ForceInstall}, + + [switch] + ${AutoReboot}, + + [switch] + ${IgnoreReboot}, + + [datetime] + ${ScheduleReboot}, + + [Parameter(ParameterSetName = 'ServiceID')] + [string] + ${ServiceID}, + + [Parameter(ParameterSetName = 'WindowsUpdate')] + [switch] + ${WindowsUpdate}, + + [Parameter(ParameterSetName = 'MicrosoftUpdate')] + [switch] + ${MicrosoftUpdate}, + + [string] + ${Criteria}, + + [ValidateSet('Driver', 'Software')] + [string] + ${UpdateType}, + + [ValidateSet('Installation', 'Uninstallation')] + [string] + ${DeploymentAction}, + + [switch] + ${IsAssigned}, + + [switch] + ${IsPresent}, + + [switch] + ${BrowseOnly}, + + [switch] + ${AutoSelectOnWebSites}, + + [string[]] + ${UpdateID}, + + [string[]] + ${NotUpdateID}, + + [int] + ${RevisionNumber}, + + [string[]] + ${CategoryIDs}, + + [switch] + ${IsInstalled}, + + [switch] + ${IsHidden}, + + [switch] + ${WithHidden}, + + [switch] + ${ShowPreSearchCriteria}, + + [ValidateSet('Critical Updates', 'Definition Updates', 'Drivers', 'Feature Packs', 'Security Updates', 'Service Packs', 'Tools', 'Update Rollups', 'Updates', 'Upgrades', 'Microsoft')] + [string[]] + ${RootCategories}, + + [string[]] + ${Category}, + + [string[]] + ${KBArticleID}, + + [string] + ${Title}, + + [ValidateSet('Critical', 'Important', 'Moderate', 'Low', 'Unspecified')] + [string[]] + ${Severity}, + + [string[]] + ${NotCategory}, + + [string[]] + ${NotKBArticleID}, + + [string] + ${NotTitle}, + + [ValidateSet('Critical', 'Important', 'Moderate', 'Low', 'Unspecified')] + [string[]] + ${NotSeverity}, + + [Alias('Silent')] + [switch] + ${IgnoreUserInput}, + + [switch] + ${IgnoreRebootRequired}, + + [switch] + ${AutoSelectOnly}, + + [long] + ${MaxSize}, + + [long] + ${MinSize}, + + [switch] + ${Debuger}) + +begin { + try { + $outBuffer = $null + if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer)) { + $PSBoundParameters['OutBuffer'] = 1 + } + + $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('PSWindowsUpdate\Get-WindowsUpdate', [System.Management.Automation.CommandTypes]::Cmdlet) + $scriptCmd = { & $wrappedCmd @PSBoundParameters } + + $steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin) + $steppablePipeline.Begin($PSCmdlet) + } catch { + throw + } +} + +process { + try { + $steppablePipeline.Process($_) + } catch { + throw + } +} + +end { + try { + $steppablePipeline.End() + } catch { + throw + } +} + +clean { + if ($null -ne $steppablePipeline) { + $steppablePipeline.Clean() + } +} +<# + +.ForwardHelpTargetName PSWindowsUpdate\Get-WindowsUpdate +.ForwardHelpCategory Cmdlet + +#> \ No newline at end of file From 82d47d25bcff2e8e9e657b9da5c24a4d5f2b8175 Mon Sep 17 00:00:00 2001 From: Adam Bertram Date: Mon, 3 Jun 2024 13:46:30 -0500 Subject: [PATCH 14/19] added get-windowsupdate proxy function and install-windowsupdate tweak --- Windows Update/Get-WindowsUpdate.ps1 | 318 +++++++++++++++++++++++ Windows Update/Install-WindowsUpdate.ps1 | 7 - 2 files changed, 318 insertions(+), 7 deletions(-) create mode 100644 Windows Update/Get-WindowsUpdate.ps1 diff --git a/Windows Update/Get-WindowsUpdate.ps1 b/Windows Update/Get-WindowsUpdate.ps1 new file mode 100644 index 0000000..6ce4b58 --- /dev/null +++ b/Windows Update/Get-WindowsUpdate.ps1 @@ -0,0 +1,318 @@ +#requires -modules @{ModuleName='PSWindowsUpdate'; ModuleVersion='2.2.1.4'} + +<# +.SYNOPSIS + This script retrieves Windows updates on a local computer using various parameters to control the update process. + +.DESCRIPTION + The script utilizes the PSWindowsUpdate module to get Windows updates. It provides options to send reports, schedule updates, + accept all updates, hide updates, and specify various criteria for update selection. The script supports Windows Update and + Microsoft Update services. + +.PARAMETER SendReport + Sends a report of the update status. + +.PARAMETER PSWUSettings + A hashtable of settings for the PSWindowsUpdate module. + +.PARAMETER SendHistory + Sends the update history report. + +.PARAMETER ScheduleJob + Schedules the update job to run at a specified date and time. + +.PARAMETER AcceptAll + Automatically accepts all updates. + +.PARAMETER RecurseCycle + Specifies the number of recursive cycles for checking updates. + +.PARAMETER Hide + Hides the specified updates. + +.PARAMETER ServiceID + Specifies the service ID for the updates (parameter set: 'ServiceID'). + +.PARAMETER WindowsUpdate + Specifies to use Windows Update (parameter set: 'WindowsUpdate'). + +.PARAMETER MicrosoftUpdate + Specifies to use Microsoft Update (parameter set: 'MicrosoftUpdate'). + +.PARAMETER Criteria + Specifies criteria for selecting updates. + +.PARAMETER UpdateType + Specifies the type of updates to manage (Driver or Software). + +.PARAMETER DeploymentAction + Specifies the deployment action (Installation or Uninstallation). + +.PARAMETER IsAssigned + Filters updates that are assigned. + +.PARAMETER IsPresent + Filters updates that are present. + +.PARAMETER BrowseOnly + Browses for updates without installing them. + +.PARAMETER AutoSelectOnWebSites + Automatically selects updates on websites. + +.PARAMETER UpdateID + Specifies the IDs of updates to include. + +.PARAMETER NotUpdateID + Specifies the IDs of updates to exclude. + +.PARAMETER RevisionNumber + Specifies the revision number of updates. + +.PARAMETER CategoryIDs + Specifies the category IDs of updates. + +.PARAMETER IsInstalled + Filters updates that are installed. + +.PARAMETER IsHidden + Filters updates that are hidden. + +.PARAMETER WithHidden + Includes hidden updates. + +.PARAMETER ShowPreSearchCriteria + Shows the pre-search criteria. + +.PARAMETER RootCategories + Specifies the root categories of updates (e.g., Critical Updates, Security Updates, etc.). + +.PARAMETER Category + Specifies categories of updates to include. + +.PARAMETER KBArticleID + Specifies the KB article IDs of updates. + +.PARAMETER Title + Specifies the title of updates. + +.PARAMETER Severity + Specifies the severity of updates (e.g., Critical, Important, etc.). + +.PARAMETER NotCategory + Specifies categories of updates to exclude. + +.PARAMETER NotKBArticleID + Specifies the KB article IDs of updates to exclude. + +.PARAMETER NotTitle + Specifies the title of updates to exclude. + +.PARAMETER NotSeverity + Specifies the severity of updates to exclude. + +.PARAMETER IgnoreUserInput + Aliased as 'Silent', ignores user input during the update process. + +.PARAMETER IgnoreRebootRequired + Ignores reboot requirements after updates are installed. + +.PARAMETER AutoSelectOnly + Automatically selects updates to be installed. + +.PARAMETER MaxSize + Specifies the maximum size of updates to include. + +.PARAMETER MinSize + Specifies the minimum size of updates to include. + +.PARAMETER Debuger + Enables debugging for the update process. + +.NOTES + Requires the 'PSWindowsUpdate' module version 2.2.1.4 or higher. + For more information on PSWindowsUpdate, visit: https://github.com/PSWindowsUpdate/PSWindowsUpdate + +.EXAMPLE + PS> .\Manage-WindowsUpdate.ps1 -Install -AutoReboot + + This example installs all available updates and automatically reboots the computer after installation. + +.EXAMPLE + PS> .\Manage-WindowsUpdate.ps1 -Download -SendReport + + This example downloads all available updates and sends a report of the update status. +#> + +[CmdletBinding(DefaultParameterSetName = 'Default', SupportsShouldProcess = $true, ConfirmImpact = 'High')] +param( + [switch] + ${SendReport}, + + [hashtable] + ${PSWUSettings}, + + [switch] + ${SendHistory}, + + [datetime] + ${ScheduleJob}, + + [switch] + ${AcceptAll}, + + [int] + ${RecurseCycle}, + + [switch] + ${Hide}, + + [Parameter(ParameterSetName = 'ServiceID')] + [string] + ${ServiceID}, + + [Parameter(ParameterSetName = 'WindowsUpdate')] + [switch] + ${WindowsUpdate}, + + [Parameter(ParameterSetName = 'MicrosoftUpdate')] + [switch] + ${MicrosoftUpdate}, + + [string] + ${Criteria}, + + [ValidateSet('Driver', 'Software')] + [string] + ${UpdateType}, + + [ValidateSet('Installation', 'Uninstallation')] + [string] + ${DeploymentAction}, + + [switch] + ${IsAssigned}, + + [switch] + ${IsPresent}, + + [switch] + ${BrowseOnly}, + + [switch] + ${AutoSelectOnWebSites}, + + [string[]] + ${UpdateID}, + + [string[]] + ${NotUpdateID}, + + [int] + ${RevisionNumber}, + + [string[]] + ${CategoryIDs}, + + [switch] + ${IsInstalled}, + + [switch] + ${IsHidden}, + + [switch] + ${WithHidden}, + + [switch] + ${ShowPreSearchCriteria}, + + [ValidateSet('Critical Updates', 'Definition Updates', 'Drivers', 'Feature Packs', 'Security Updates', 'Service Packs', 'Tools', 'Update Rollups', 'Updates', 'Upgrades', 'Microsoft')] + [string[]] + ${RootCategories}, + + [string[]] + ${Category}, + + [string[]] + ${KBArticleID}, + + [string] + ${Title}, + + [ValidateSet('Critical', 'Important', 'Moderate', 'Low', 'Unspecified')] + [string[]] + ${Severity}, + + [string[]] + ${NotCategory}, + + [string[]] + ${NotKBArticleID}, + + [string] + ${NotTitle}, + + [ValidateSet('Critical', 'Important', 'Moderate', 'Low', 'Unspecified')] + [string[]] + ${NotSeverity}, + + [Alias('Silent')] + [switch] + ${IgnoreUserInput}, + + [switch] + ${AutoSelectOnly}, + + [long] + ${MaxSize}, + + [long] + ${MinSize}, + + [switch] + ${Debuger}) + +begin { + try { + $outBuffer = $null + if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer)) { + $PSBoundParameters['OutBuffer'] = 1 + } + + $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('PSWindowsUpdate\Get-WindowsUpdate', [System.Management.Automation.CommandTypes]::Cmdlet) + $scriptCmd = { & $wrappedCmd @PSBoundParameters } + + $steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin) + $steppablePipeline.Begin($PSCmdlet) + } catch { + throw + } +} + +process { + try { + $steppablePipeline.Process($_) + } catch { + throw + } +} + +end { + try { + $steppablePipeline.End() + } catch { + throw + } +} + +clean { + if ($null -ne $steppablePipeline) { + $steppablePipeline.Clean() + } +} +<# + +.ForwardHelpTargetName PSWindowsUpdate\Get-WindowsUpdate +.ForwardHelpCategory Cmdlet + +#> diff --git a/Windows Update/Install-WindowsUpdate.ps1 b/Windows Update/Install-WindowsUpdate.ps1 index a52fa1f..17f2884 100644 --- a/Windows Update/Install-WindowsUpdate.ps1 +++ b/Windows Update/Install-WindowsUpdate.ps1 @@ -9,9 +9,6 @@ and specifying the updates to be managed, including scheduling options, criteria for selection, and actions to be taken. The script also supports reporting and history features, allowing for detailed control and monitoring of update activities. -.PARAMETER ComputerName - The name of the computer(s) on which to manage Windows updates. - .PARAMETER SendReport Sends a report of the update status. @@ -171,10 +168,6 @@ [CmdletBinding(DefaultParameterSetName = 'Default', SupportsShouldProcess = $true, ConfirmImpact = 'High')] param( - [Parameter(ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] - [string[]] - ${ComputerName}, - [switch] ${SendReport}, From 461f1d3cc5b283899462b7cd83b06c470ea961e8 Mon Sep 17 00:00:00 2001 From: Adam Bertram Date: Mon, 3 Jun 2024 13:54:06 -0500 Subject: [PATCH 15/19] added restart-computer proxy function --- Utility/Restart-Computer.ps1 | 137 +++++++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 Utility/Restart-Computer.ps1 diff --git a/Utility/Restart-Computer.ps1 b/Utility/Restart-Computer.ps1 new file mode 100644 index 0000000..9285162 --- /dev/null +++ b/Utility/Restart-Computer.ps1 @@ -0,0 +1,137 @@ +<# +.SYNOPSIS + Restarts the specified computer(s). + +.DESCRIPTION + This script uses the Restart-Computer cmdlet from the Microsoft.PowerShell.Management module to restart one or more computers. + It supports various authentication methods and can be configured to wait for the restart to complete, with options for timeout + and delay between checks. + +.PARAMETER WsmanAuthentication + Specifies the authentication method to be used for the WSMan connection. Valid values are 'Default', 'Basic', 'Negotiate', + 'CredSSP', 'Digest', and 'Kerberos'. + +.PARAMETER ComputerName + Specifies the computer(s) to restart. This can be a name, alias, or IP address. This parameter supports pipeline input. + +.PARAMETER Credential + Specifies the user account credentials to use for the restart operation. + +.PARAMETER Force + Forces the restart of the computer(s) without prompting for confirmation. + +.PARAMETER Wait + Waits for the restart to complete before continuing. + +.PARAMETER Timeout + Specifies the maximum amount of time (in seconds) to wait for the computer(s) to restart. A value of -1 means to wait indefinitely. + +.PARAMETER For + Specifies the service types to wait for after the restart. This is used in conjunction with the Wait parameter. + +.PARAMETER Delay + Specifies the delay (in seconds) between checks to determine if the computer(s) have restarted. + +.NOTES + For more information, visit: https://go.microsoft.com/fwlink/?LinkID=2097060 + +.EXAMPLE + PS> .\Restart-Computer.ps1 -ComputerName 'Server01' -Credential (Get-Credential) -Force -Wait -Timeout 300 + + This example restarts 'Server01' using specified credentials, forces the restart without prompting for confirmation, + waits for the restart to complete, and specifies a timeout of 300 seconds. + +.EXAMPLE + PS> .\Restart-Computer.ps1 -ComputerName 'Server01','Server02' -WsmanAuthentication 'Kerberos' -Wait -Delay 10 + + This example restarts 'Server01' and 'Server02' using Kerberos authentication, waits for the restart to complete, + and sets a delay of 10 seconds between checks. +#> + + +[CmdletBinding(DefaultParameterSetName = 'DefaultSet', SupportsShouldProcess = $true, ConfirmImpact = 'Medium', HelpUri = 'https://go.microsoft.com/fwlink/?LinkID=2097060', RemotingCapability = 'OwnedByCommand')] +param( + [Parameter(ParameterSetName = 'DefaultSet')] + [ValidateSet('Default', 'Basic', 'Negotiate', 'CredSSP', 'Digest', 'Kerberos')] + [string] + ${WsmanAuthentication}, + + [Parameter(Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] + [Alias('CN', '__SERVER', 'Server', 'IPAddress')] + [ValidateNotNullOrEmpty()] + [string[]] + ${ComputerName}, + + [Parameter(Position = 1)] + [ValidateNotNullOrEmpty()] + [pscredential] + [System.Management.Automation.CredentialAttribute()] + ${Credential}, + + [Alias('f')] + [switch] + ${Force}, + + [Parameter(ParameterSetName = 'DefaultSet')] + [switch] + ${Wait}, + + [Parameter(ParameterSetName = 'DefaultSet')] + [Alias('TimeoutSec')] + [ValidateRange(-1, 2147483647)] + [int] + ${Timeout}, + + [Parameter(ParameterSetName = 'DefaultSet')] + [Microsoft.PowerShell.Commands.WaitForServiceTypes] + ${For}, + + [Parameter(ParameterSetName = 'DefaultSet')] + [ValidateRange(1, 32767)] + [short] + ${Delay}) + +begin { + try { + $outBuffer = $null + if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer)) { + $PSBoundParameters['OutBuffer'] = 1 + } + + $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Microsoft.PowerShell.Management\Restart-Computer', [System.Management.Automation.CommandTypes]::Cmdlet) + $scriptCmd = { & $wrappedCmd @PSBoundParameters } + + $steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin) + $steppablePipeline.Begin($PSCmdlet) + } catch { + throw + } +} + +process { + try { + $steppablePipeline.Process($_) + } catch { + throw + } +} + +end { + try { + $steppablePipeline.End() + } catch { + throw + } +} + +clean { + if ($null -ne $steppablePipeline) { + $steppablePipeline.Clean() + } +} +<# + +.ForwardHelpTargetName Microsoft.PowerShell.Management\Restart-Computer +.ForwardHelpCategory Cmdlet + +#> \ No newline at end of file From 3588e13089a873aacf94b4db86ea663c75bd1c1d Mon Sep 17 00:00:00 2001 From: Adam Bertram Date: Tue, 4 Jun 2024 13:42:16 -0500 Subject: [PATCH 16/19] added convert-certificate --- Utility/Convert-Certificate.ps1 | 152 ++++++++++++++++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 Utility/Convert-Certificate.ps1 diff --git a/Utility/Convert-Certificate.ps1 b/Utility/Convert-Certificate.ps1 new file mode 100644 index 0000000..b1bccf7 --- /dev/null +++ b/Utility/Convert-Certificate.ps1 @@ -0,0 +1,152 @@ +<# +.SYNOPSIS + Converts a certificate from one format to another. + +.DESCRIPTION + This script converts a certificate file from one format to another, supporting PFX, PEM, and DER formats. + It handles the conversion based on the source and destination file extensions, and optionally uses a password + for PFX files. The script can also force overwrite the destination file if it already exists. + +.PARAMETER SourcePath + The path to the source certificate file. The file must exist. + +.PARAMETER DestinationPath + The path to the destination certificate file. The script will convert the source certificate to this file. + +.PARAMETER Password + The password for the PFX certificate, if applicable. + +.PARAMETER Force + Forces the script to overwrite the destination file if it already exists. + +.NOTES + To validate the converted certificates, you can use the following OpenSSL commands: + - For PEM: `openssl x509 -in ./output.pem -inform pem -text -noout` + - For DER: `openssl verify -CAfile ./output.der ./output.der` + - For PFX: `openssl pkcs12 -info -in ./output.pfx` + +.EXAMPLE + PS> .\Convert-Certificate.ps1 -SourcePath 'C:\path\to\cert.pfx' -DestinationPath 'C:\path\to\cert.pem' -Password (ConvertTo-SecureString 'password' -AsPlainText -Force) -Force + + This example converts a PFX certificate to a PEM certificate, using a specified password and forcing overwrite of the destination file. + +.EXAMPLE + PS> .\Convert-Certificate.ps1 -SourcePath 'C:\path\to\cert.pem' -DestinationPath 'C:\path\to\cert.der' + + This example converts a PEM certificate to a DER certificate. + +.EXAMPLE + PS> .\Convert-Certificate.ps1 -SourcePath 'C:\path\to\cert.der' -DestinationPath 'C:\path\to\cert.pem' + + This example converts a DER certificate to a PEM certificate. +#> +[CmdletBinding()] +param ( + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [ValidateScript( + { Test-Path -Path $_ -PathType Leaf }, + ErrorMessage = 'The file [{0}] does not exist.' + )] + [string]$SourcePath, + + [Parameter(Mandatory)] + [ValidateNotNullOrEmpty()] + [string]$DestinationPath, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [securestring]$Password, + + [Parameter()] + [switch]$Force +) + +$ErrorActionPreference = 'Stop' + +# Function to extract file extension and convert to certificate type +function Get-CertificateType { + param ( + [string]$Path + ) + $extension = [System.IO.Path]::GetExtension($Path).TrimStart('.') + switch ($extension.ToUpper()) { + 'PFX' { return 'PFX' } + 'PEM' { return 'PEM' } + 'DER' { return 'DER' } + default { throw "Unsupported file extension: $extension" } + } +} + +try { + $SourceType = Get-CertificateType -Path $SourcePath + $DestinationType = Get-CertificateType -Path $DestinationPath + + # Check if destination file exists and handle based on $Force parameter + if (Test-Path -Path $DestinationPath -PathType Leaf) { + if (-not $Force) { + throw "The destination file already exists. Use -Force to overwrite." + } + } + + switch ($SourceType) { + 'PFX' { + $pfxPassword = $Password | ConvertFrom-SecureString -AsPlainText + $cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($SourcePath, $pfxPassword) + + if ($DestinationType -eq 'PEM') { + $pemBytes = [System.Text.Encoding]::UTF8.GetBytes($cert.Export([System.Security.Cryptography.X509Certificates.X509ContentType]::Cert)) + [System.IO.File]::WriteAllBytes($DestinationPath, $pemBytes) + } elseif ($DestinationType -eq 'DER') { + $derBytes = $cert.Export([System.Security.Cryptography.X509Certificates.X509ContentType]::Cert) + [System.IO.File]::WriteAllBytes($DestinationPath, $derBytes) + } + } + 'PEM' { + if ($DestinationType -eq 'PFX') { + $pfxPassword = $Password | ConvertFrom-SecureString -AsPlainText + $pemContent = Get-Content $SourcePath -Raw + $certCollection = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2Collection + $certCollection.ImportFromPem($pemContent) + + # Assuming you want to export the first certificate in the collection + $cert = $certCollection[0] + $bytes = $cert.Export([System.Security.Cryptography.X509Certificates.X509ContentType]::Pfx, $pfxPassword) + [System.IO.File]::WriteAllBytes($DestinationPath, $bytes) + } elseif ($DestinationType -eq 'DER') { + $pemContent = Get-Content $SourcePath -Raw + $certCollection = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2Collection + $certCollection.ImportFromPem($pemContent) + + # Assuming you want to export the first certificate in the collection + $cert = $certCollection[0] + $derBytes = $cert.Export([System.Security.Cryptography.X509Certificates.X509ContentType]::Cert) + [System.IO.File]::WriteAllBytes($DestinationPath, $derBytes) + } + } + 'DER' { + if ($DestinationType -eq 'PFX') { + throw "Cannot convert from DER to PFX without the private key." + } elseif ($DestinationType -eq 'PEM') { + try { + $derBytes = [System.IO.File]::ReadAllBytes($SourcePath) + # Ensure that the byte array is not empty or malformed + if ($derBytes.Length -eq 0) { + throw "The DER file appears to be empty or invalid." + } + + # Load the certificate from DER bytes using the constructor + $cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($derBytes, $null, [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::DefaultKeySet) + + # Export the certificate to PEM format + $pemBytes = [System.Text.Encoding]::UTF8.GetBytes("-----BEGIN CERTIFICATE-----`n" + [System.Convert]::ToBase64String($cert.RawData, 'InsertLineBreaks') + "`n-----END CERTIFICATE-----") + [System.IO.File]::WriteAllBytes($DestinationPath, $pemBytes) + } catch { + throw "Failed to load or convert the DER file: $($_.Exception.Message)" + } + } + } + } +} catch { + throw "An error occurred during the certificate conversion: $($_.Exception.Message)" +} \ No newline at end of file From b15767f58bafea3d7a8357ca7745a1d4babb767a Mon Sep 17 00:00:00 2001 From: Adam Bertram Date: Wed, 5 Jun 2024 13:41:10 -0500 Subject: [PATCH 17/19] added Get-WindowsEventLogRecord --- Utility/Get-WindowsEventLogRecord.ps1 | 129 ++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 Utility/Get-WindowsEventLogRecord.ps1 diff --git a/Utility/Get-WindowsEventLogRecord.ps1 b/Utility/Get-WindowsEventLogRecord.ps1 new file mode 100644 index 0000000..96f905b --- /dev/null +++ b/Utility/Get-WindowsEventLogRecord.ps1 @@ -0,0 +1,129 @@ +# Requires -Version 7.0 + +<# +.SYNOPSIS + Retrieves events from the event log based on specified criteria. + +.DESCRIPTION + This script uses the Get-WinEvent cmdlet to query and retrieve events from the event log. It supports filtering by log name, + event levels, event IDs, sources, and time ranges. The script builds an XPath query string based on the provided parameters + to fetch the matching events. + +.PARAMETER LogName + Specifies the name of the event log to query. + +.PARAMETER Level + Specifies the level(s) of the events to retrieve. Valid values are 'Error', 'Warning', 'Information', 'Critical', and 'Verbose'. + +.PARAMETER EventID + Specifies the event ID(s) of the events to retrieve. + +.PARAMETER Source + Specifies the source(s) of the events to retrieve. + +.PARAMETER StartTime + Specifies the start time for the events to retrieve. Only events created on or after this time are retrieved. + +.PARAMETER EndTime + Specifies the end time for the events to retrieve. Only events created on or before this time are retrieved. + +.PARAMETER MaxEvents + Specifies the maximum number of events to retrieve. + +.NOTES + Requires PowerShell version 7.0 or later. + For more information on Get-WinEvent, visit: https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.diagnostics/get-winevent + +.EXAMPLE + PS> .\Get-WindowsEventLogRecord.ps1 -LogName 'Application' -Level 'Error', 'Warning' -StartTime (Get-Date).AddDays(-1) + + This example retrieves error and warning events from the 'Application' log that were created in the last day. + +.EXAMPLE + PS> .\Get-WindowsEventLogRecord.ps1 -LogName 'System' -EventID 1000, 2000 -Source 'Microsoft-Windows-Winlogon' + + This example retrieves events with IDs 1000 and 2000 from the 'System' log, where the source is 'Microsoft-Windows-Winlogon'. + +.EXAMPLE + PS> .\Get-WindowsEventLogRecord.ps1 -LogName 'Security' -Level 'Information' -MaxEvents 50 + + This example retrieves the top 50 informational events from the 'Security' log. +#> + + +[CmdletBinding()] +param ( + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$LogName, + + [Parameter()] + [ValidateSet("Error", "Warning", "Information", "Critical", "Verbose")] + [string[]]$Level, + + [Parameter()] + [int[]]$EventID, + + [Parameter()] + [string[]]$Source, + + [Parameter()] + [datetime]$StartTime, + + [Parameter()] + [datetime]$EndTime, + + [Parameter()] + [int]$MaxEvents +) + +$params = @{} + +if ($PSBoundParameters.ContainsKey('LogName')) { + $params.LogName = $LogName +} + +$xpathQuery = "*[System[" +$conditions = @() +if ($EventID) { $conditions += "EventID=" + ($EventID -join ' or EventID=') } +if ($Level) { + $levelMapping = @{ + 'Critical' = 1 + 'Error' = 2 + 'Warning' = 3 + 'Information' = 4 + 'Verbose' = 5 + } + $levelConditions = $Level | ForEach-Object { "Level=$($levelMapping[$_])" } + $conditions += "(" + ($levelConditions -join ' or ') + ")" +} + +if ($StartTime) { + $conditions += "TimeCreated[@SystemTime>='$($StartTime.ToString("s"))']" +} +if ($EndTime) { + $conditions += "TimeCreated[@SystemTime<='$($EndTime.ToString("s"))']" +} + +if ($Source) { + $conditions += "(Provider[@Name='" + ($Source -join "'] or Provider[@Name='") + "'])" +} + +if ($conditions.Count -gt 0) { + $xpathQuery += $conditions -join ' and ' +} +$xpathQuery += "]]" + +# Validate that at least one filtering parameter is provided +if ($xpathQuery -eq "*[System[]]" -and -not $LogName) { + throw "You must specify at least one filtering parameter (LogName, EventID, Level, Source, User, Message, StartTime, or EndTime)." +} + +$params.FilterXPath = $xpathQuery + +try { + Get-WinEvent @params | Select-Object TimeCreated, Id, LevelDisplayName, ProviderName, UserId, MachineName, LogName, Message +} catch { + Write-Error "An error occurred while retrieving events: $($_.Exception.Message)" +} + From 28209dcb86277502fa37da44c4c607bb31f69de9 Mon Sep 17 00:00:00 2001 From: Adam Bertram Date: Fri, 7 Jun 2024 12:25:53 -0500 Subject: [PATCH 18/19] added Get-UserLogonHistory --- Security/Get-UserLogonHistory.ps1 | 196 ++++++++++++++++++++++++++++++ 1 file changed, 196 insertions(+) create mode 100644 Security/Get-UserLogonHistory.ps1 diff --git a/Security/Get-UserLogonHistory.ps1 b/Security/Get-UserLogonHistory.ps1 new file mode 100644 index 0000000..41346a6 --- /dev/null +++ b/Security/Get-UserLogonHistory.ps1 @@ -0,0 +1,196 @@ +<# +.SYNOPSIS + Retrieves user session information from event logs, including logon and logoff times. + +.DESCRIPTION + This script queries the event logs to determine user session activity, including logon and logoff events. It handles various + session start and stop events, processes the events to extract relevant information such as username, logon ID, and logon type, + and attempts to match logoff events to logon events to calculate session duration. + +.NOTES + More information about XPath queries in event logs: https://docs.microsoft.com/en-us/windows/win32/wes/querying-for-event-data + +.EXAMPLE + PS> Get-UserLogonHistory + + Retrieves a list of historical user sessions. +#> + +[CmdletBinding()] +param +() + +function Get-LoggedInUser { + Get-Process -IncludeUserName | Where-Object { $_.UserName -and $_.UserName -notmatch "^NT *|^Window Manager" } | Select-Object -ExpandProperty UserName -Unique +} + +function GetEventUserName { + param ( + [Parameter(Mandatory)] + [System.Xml.XmlDocument]$EventRecord + ) + + $script:eventNamespace.AddNamespace("evt", "http://schemas.microsoft.com/win/2004/08/events/event") + $targetUserName = $EventRecord.SelectSingleNode("//evt:Data[@Name='TargetUserName']", $script:eventNamespace).InnerText + $targetDomainName = $EventRecord.SelectSingleNode("//evt:Data[@Name='TargetDomainName']", $script:eventNamespace).InnerText + + if ($targetDomainName) { + "$targetDomainName\$targetUserName" + } else { + $targetUserName + } + +} + +function GetEventLogonType { + param ( + [Parameter(Mandatory)] + [System.Xml.XmlDocument]$EventRecord + ) + + ($EventRecord.Event.EventData.Data | Where-Object { $_.Name -eq 'LogonType' }).'#text' +} + +function GetUserLogonId { + param ( + [Parameter(Mandatory)] + [System.Xml.XmlDocument]$EventRecord + ) + + $userLogonId = ($EventRecord.Event.EventData.Data | Where-Object { $_.Name -eq 'TargetLogonId' }).'#text' + if (-not $userLogonId) { + $userLogonId = ($EventRecord.Event.EventData.Data | Where-Object { $_.Name -eq 'LogonId' }).'#text' + } + + $userLogonId +} + +function GetLogoffEvent { + param ( + [Parameter()] + [ValidateNotNullOrEmpty()] + [datetime]$LogonStartTime, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [string]$UserLogonId + ) + + ## This assumes event is the most recent one that matches the criteria + @($script:Events).where({ + $xEvt = [xml]$_.ToXml() + [datetime]$_.TimeCreated -gt $LogonStartTime -and + $_.ID -in $script:sessionStopIds -and + (GetUserLogonId -EventRecord $xEvt) -eq $UserLogonId + }) | Select-Object -Last 1 +} + +try { + + #region Defie all of the events to indicate session start or top + $sessionEvents = @( + @{ 'Label' = 'Logon'; 'EventType' = 'SessionStart'; 'LogName' = 'Security'; 'ID' = 4624 } ## Advanced Audit Policy --> System Audit Policies --> Logon/Logoff --> Audit Logon + @{ 'Label' = 'Logoff'; 'EventType' = 'SessionStop'; 'LogName' = 'Security'; 'ID' = 4647 } ## Advanced Audit Policy --> System Audit Policies --> Logon/Logoff --> Audit Logoff + @{ 'Label' = 'Startup'; 'EventType' = 'SessionStop'; 'LogName' = 'System'; 'ID' = 6005 } ## Audit Policy --> Audit System Events + @{ 'Label' = 'RdpSessionReconnect'; 'EventType' = 'SessionStart'; 'LogName' = 'Security'; 'ID' = 4778 } ## Advanced Audit Policy --> System Audit Policies --> Logon/Logoff --> Audit Other Logon/Logoff Events + @{ 'Label' = 'RdpSessionDisconnect'; 'EventType' = 'SessionStop'; 'LogName' = 'Security'; 'ID' = 4779 } ## Advanced Audit Policy --> System Audit Policies --> Logon/Logoff --> Audit Other Logon/Logoff Events + @{ 'Label' = 'Locked'; 'EventType' = 'SessionStop'; 'LogName' = 'Security'; 'ID' = 4800 } ## Advanced Audit Policy --> System Audit Policies --> Logon/Logoff --> Audit Other Logon/Logoff Events + @{ 'Label' = 'Unlocked'; 'EventType' = 'SessionStart'; 'LogName' = 'Security'; 'ID' = 4801 } ## Advanced Audit Policy --> System Audit Policies --> Logon/Logoff --> Audit Other Logon/Logoff Events + ) + + ## All of the IDs that designate when user activity starts + $sessionStartIds = ($sessionEvents | Where-Object { $_.EventType -eq 'SessionStart' }).ID + ## All of the IDs that designate when user activity stops + $script:sessionStopIds = ($sessionEvents | Where-Object { $_.EventType -eq 'SessionStop' }).ID + #endregion + + ## Define all of the log names we'll be querying + $logNames = ($sessionEvents.LogName | Select-Object -Unique) + + ## These are not all logon types but only those that are relevant to user activity (not internal services) + $userLogonTypes = @{ + 2 = "Interactive" + 7 = "Unlock" + 10 = "RemoteInteractive" + 11 = "CachedInteractive" + } + + ## Build the XPath query for the security event log in order to query events as fast as possible + ## It would be better if TargetDomainName could be filtered by anything with a space in it but due to XPath 1.0 + ## restrictions, it's not possible. + $xPath = @" +*[ + ( + EventData[Data[@Name='TargetDomainName'] != 'Window Manager'] and + EventData[Data[@Name='TargetDomainName'] != 'NT AUTHORITY'] and + EventData[Data[@Name='TargetDomainName'] != 'Font Driver Host'] + ) and + (EventData[Data[@Name='LogonType'] = '$($userLogonTypes.Keys -join "'] or + EventData[Data[@Name='LogonType'] = '")'] + ) or + ( + System[ + (EventID=$($script:sessionStopIds -join " or EventID=")) + ] + ) +] +"@ + ## Query the computer's event logs using the Xpath filter + if (-not ($script:events = Get-WinEvent -LogName $logNames -FilterXPath $xPath)) { + Write-Warning -Message 'No logon events found'. + } else { + $script:eventNamespace = New-Object System.Xml.XmlNamespaceManager(([xml]$script:events[0].ToXml()).NameTable) + Write-Verbose -Message "Found [$($script:events.Count)] events to look through" + + $loggedInUsers = Get-LoggedInUser + + ## Find all user start activity events and begin parsing + @($script:events).where({ $_.Id -in $sessionStartIds }).foreach({ + try { + $xEvt = [xml]$_.ToXml() + + $userName = GetEventUserName -Event $xEvt + $logonEvtId = $_.Id + + $startTime = $_.TimeCreated + + $userLogonId = GetUserLogonId -EventRecord $xEvt + + $userLogonType = GetEventLogonType -EventRecord $xEvt + + Write-Verbose -Message "New session start event found: event ID [$logonEvtId] username [$userName] user logonID [$($userLogonId)] user logon type [$userLogonType] time [$($startTime)]" + ## Try to match up the user activity end event with the start event we're processing + if (-not ($logoffEvent = GetLogoffEvent -LogonStartTime $startTime -UserLogonId $userLogonId)) { + ## If no logoff event is found, the user might still be logged in + if ($userName -in $loggedInUsers) { + $stopTime = Get-Date + $stopAction = 'Still logged in' + } else { + throw "Could not find a session end event for logon ID [$($userLogonId)] username [$userName] start event time [$startTime]." + } + } else { + ## Capture the user activity end time + $stopTime = $logoffEvent.TimeCreated + Write-Verbose -Message "Session stop event ID is [$($logoffEvent.Id)]" + $stopAction = @($sessionEvents).where({ $_.ID -eq $logoffEvent.Id }).Label + } + + $sessionTimespan = New-TimeSpan -Start $startTime -End $stopTime + + [pscustomobject]@{ + 'Username' = $userName + 'UserLogonId' = $userLogonId + 'StartTime' = $startTime + 'StartAction' = @($sessionEvents).where({ $_.ID -eq $logonEvtId }).Label + 'StopTime' = $stopTime ? $stopTime : 'Still logged in' + 'StopAction' = $stopTime ? $stopAction : 'Still logged in' + 'Session Active Time' = $sessionTimespan + } + } catch { + Write-Warning -Message $_.Exception.Message + } + }) + } +} catch { + $PSCmdlet.ThrowTerminatingError($_) +} \ No newline at end of file From b5b12bc8d014c8ea395b6f943286ca3072a5fcc1 Mon Sep 17 00:00:00 2001 From: Adam Bertram Date: Fri, 7 Jun 2024 12:30:14 -0500 Subject: [PATCH 19/19] added get-windowsuptime --- Utility/Get-WindowsUptime.ps1 | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 Utility/Get-WindowsUptime.ps1 diff --git a/Utility/Get-WindowsUptime.ps1 b/Utility/Get-WindowsUptime.ps1 new file mode 100644 index 0000000..d6de172 --- /dev/null +++ b/Utility/Get-WindowsUptime.ps1 @@ -0,0 +1,22 @@ +<# +.SYNOPSIS + Retrieves the system uptime. + +.DESCRIPTION + This script calculates the duration the system has been up since the last boot. It uses CIM (Common Information Model) to query + the Win32_OperatingSystem class and obtain the last boot-up time. Then, it calculates the difference between the current date and + the last boot-up time to determine the uptime duration. + +.EXAMPLE + PS> .\Get-WindowsUptime.ps1 + + Retrieves and displays the system uptime. +#> + +[CmdletBinding()] +param () + +$uptime = Get-CimInstance -ClassName Win32_OperatingSystem | Select-Object -ExpandProperty LastBootUpTime + +# Calculate the uptime duration +(Get-Date) - $uptime \ No newline at end of file