SnsPsModule.psm1
##### Add-SnsSecurityPolicy ======================================================= Function Add-SnsSecurityPolicy () { <# .SYNOPSIS This CmdLet Sets The PowerShell Host And Defines The SnsPsModule Module Configuration .DESCRIPTION This CmdLet Sets The PowerShell Host And Defines The SnsPsModule Module Configuration --The CmdLet Sets The PowerShell Session To Trust All Certificates Without Revocation Validation --The CmdLet Adds Support Of TLS12 Security Protocol To The PowerShell Session The Default Protocols Are Not Removed From The List To Allow Other Connections In The Same PowerShell Session --The CmdLet Generates The SnsPsModule Module Configuration Variable Required For The Remaining CmdLets In This Module .INPUTS No Inputs .OUTPUTS Global Variable [System.Object]$global:SnsModuleCfg - Contains The SnsPsModule Module Configuration .NOTES VERSION INFORMATION: - Added Trust All Certificates Without Revocation And Root Trust Checks - Added The TLS12 Security Protocol Support - Added HTML Invalid Characters Configuration - Added Transformation Entry InputField OutputField And MatchType Translation Configuration AUTHOR: Svetoslav Nedyalkov Savov THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER. .EXAMPLE Add-SnsSecurityPolicy; .LINK svesavov / SnsPsModule - https://github.com/svesavov/SnsPsModule .LINK Svetoslav Savov on LinkedIn - https://www.linkedin.com/in/svetoslavsavov #> [CmdletBinding()] Param() ##### Override The Begin Method Begin { Write-Debug "Override Begin Method"; [System.Boolean]$bolCheckForNewerVersions = [System.Environment]::UserInteractive; #================================================================================== #region PowerShell Version Verification #================================================================================== If ([System.Int32]"$($Host.Version.Major)" -lt 5) { Write-Warning "The PowerShell Version Is Less Than 5. The Module Might Not Work Properly. Use It At Your Own Risk."; } #================================================================================== #endregion PowerShell Version Verification #================================================================================== #================================================================================== #region Import Required Modules #================================================================================== ##### Import The DNS Module If ( ` (-not (Get-Module -Name "Microsoft.PowerShell.Utility" -Verbose:$false -Debug:$false)) ` -and ` (-not -not (Get-Module -Name "Microsoft.PowerShell.Utility" -ListAvailable:$true -Verbose:$false -Debug:$false)) ` ) { Write-Host "Import Microsoft.PowerShell.Utility PowerShell Module" -ForegroundColor "Green"; [System.String]$strVerbosePreference = "$($VerbosePreference)"; $VerbosePreference = "SilentlyContinue"; Import-Module -Name "Microsoft.PowerShell.Utility" -Global:$true -Force:$true -Debug:$false; $VerbosePreference = "$($strVerbosePreference)"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strVerbosePreference"; } #================================================================================== #endregion Import Required Modules #================================================================================== #================================================================================== #region Add TLS12 Protocol #================================================================================== ##### Set The Security Protocol Write-Debug "SecurityProtocol Verification and Setting."; If ("$([Net.ServicePointManager]::SecurityProtocol)" -notlike "*Tls12*") { Write-Verbose "`r`n`r`nAdding All SecurityProtocols`r`n`r`n"; [Net.ServicePointManager]::SecurityProtocol = ` @( #[Net.SecurityProtocolType]::Tls13, [Net.SecurityProtocolType]::Tls12, [Net.SecurityProtocolType]::Tls11, [Net.SecurityProtocolType]::Tls, [Net.SecurityProtocolType]::Ssl3 ); } #================================================================================== #endregion Add TLS12 Protocol #================================================================================== #================================================================================== #region Trust All Certificates Policy #================================================================================== ##### Set Trust All Certs Policy Write-Debug "Continue With CertificatePolicy Verification and Setting."; If ($false -and ("$([System.Net.ServicePointManager]::CertificatePolicy)" -notlike "TrustAllCertsPolicy")) { ##### Create The CertificatePolicy Object Using C# Write-Verbose "`r`n`r`nAdding TrustAllCertsPolicy`r`n`r`n"; Add-Type ` @" using System.Net; using System.Security.Cryptography.X509Certificates; public class TrustAllCertsPolicy : ICertificatePolicy { public bool CheckValidationResult(ServicePoint srvPoint, X509Certificate certificate, WebRequest request, int certificateProblem) { return true; } } "@ ##### Set The CertificatePolicy To Trust All Certificates [System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy; } #================================================================================== #endregion Trust All Certificates Policy #================================================================================== #================================================================================== #region Skip Certificate Validation #================================================================================== If ($false -and (-not ([System.Management.Automation.PSTypeName]"ServerCertificateValidationCallback").Type)) { Add-Type -TypeDefinition @" using System; using System.Net; using System.Net.Security; using System.Security.Cryptography.X509Certificates; public class ServerCertificateValidationCallback { public static void Ignore() { if(ServicePointManager.ServerCertificateValidationCallback == null) { ServicePointManager.ServerCertificateValidationCallback += delegate(Object obj, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors) { return true; }; } } } "@ [ServerCertificateValidationCallback]::Ignore(); } #================================================================================== #endregion Skip Certificate Validation #================================================================================== #================================================================================== #region Verify "SnsPsModule" Version #================================================================================== If ($bolCheckForNewerVersions) { ##### Get The Latest Version Of The Module From PSGallery Write-Debug "Get The Latest Version Of The Module From PSGallery"; [System.Object]$objPsGalMdl = $null; Try { [System.Object]$objPsGalMdl = Find-Module -Name "SnsPsModule" -Repository "PSGallery" -ErrorAction "Stop"; } Catch {}; ##### Get The Locally Installed Module Write-Debug "Get The Locally Installed Module"; [System.Array]$arrLocMdl = @(); [System.Array]$arrLocMdl = Get-Module -Name "SnsPsModule" -ListAvailable; ##### Check Whether SnsPsModule Is The Latest Version Write-Debug "Check Whether SnsPsModule Is The Latest Version"; If ((-not -not "$($objPsGalMdl.Version)") -and ("$($arrLocMdl[0].Version)" -ne "$($objPsGalMdl.Version)")) { Write-Warning "Module ""SnsPsModule"" Have Newer Version. Please Update It."; } } #================================================================================== #endregion Verify "SnsPsModule" Version #================================================================================== #================================================================================== #region Generate Module Configuration Variable #================================================================================== ##### Generate A Custom Object [System.Object]$objObject = New-Object -TypeName "System.Object" -Verbose:$false -Debug:$false; If ($true) { ##### Generate The PowerShell Module File SHA256 Hash $objObject | Add-Member -Force:$true -MemberType "NoteProperty" -Name "ModuleHash" -Value "$((Get-FileHash -Path ""$($PSCommandPath)"" -Algorithm ""SHA256"").Hash)"; } If ($true) { ##### Generate The System.Globalization.CultureInfo.TextInfo Object For "en-Us" Culture $objObject | Add-Member -Force:$true -MemberType "NoteProperty" -Name "TextInfo" -Value (([System.Globalization.CultureInfo]::GetCultureInfo("en-Us")).TextInfo); } If ($true) { ##### Get The Process Owner [System.String]$strPrOwner = "$(((Get-WmiObject -Class ""Win32_Process"" -Verbose:$false -Debug:$false | Where-Object {""$($_.ProcessId)"" -eq ""$($pid)""} -Verbose:$false -Debug:$false).GetOwner()).User)"; ##### Add The Process Owner To The Configuration Object $objObject | Add-Member -Force:$true -MemberType "NoteProperty" -Name "ProcessOwner" -Value "$($strPrOwner)"; ##### Reset The Variable Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strPrOwner"; } If ($true) { ##### Add The Configuration Hash As A Property Of The Master Configuration Object $objObject | Add-Member -Force:$true -MemberType "NoteProperty" -Name "HtmlIvalidChars" -Value ([Ordered]@{}); #### Add The Invalid Characters / Escaped Characters Pairs $objObject.HtmlIvalidChars.Add(' ', '%20'); $objObject.HtmlIvalidChars.Add('"', '%22'); $objObject.HtmlIvalidChars.Add('+', '%2B'); $objObject.HtmlIvalidChars.Add('&', '%26'); } ##### Generate The Master Configuration Variable If (-not (Get-Variable -Verbose:$false -Debug:$false | Where-Object {"$($_.Name)" -like "SnsModuleCfg"} -Verbose:$false -Debug:$false)) {New-Variable -Scope "Global" -Option "Constant" -Name "SnsModuleCfg" -Value ($objObject)}; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "objObject"; #================================================================================== #endregion Generate Module Configuration Variable #================================================================================== } } Add-SnsSecurityPolicy -Verbose:$false -Debug:$false -WarningAction "$($WarningPreference)" -ErrorAction "$($ErrorActionPreference)"; #================================================================================== #region Modify Host Appearance #================================================================================== ((Get-Host).UI.RawUI).WindowTitle = "SnsPsModule - Created by Svetoslav Savov"; ((Get-Host).UI.RawUI).BufferSize.Width = 120; ((Get-Host).UI.RawUI).BufferSize.Height = 50; ((Get-Host).UI.RawUI).WindowSize.Width = 120; ((Get-Host).UI.RawUI).WindowSize.Height = 50; ((Get-Host).UI.RawUI).MaxWindowSize.Width = 120; ((Get-Host).UI.RawUI).MaxWindowSize.Height = 50; If (([System.Environment]::UserInteractive) -and (-not "$($MyInvocation.PSCommandPath)")) { [System.Text.StringBuilder]$strMsg = New-Object -TypeName "System.Text.StringBuilder" -Verbose:$false -Debug:$false -ArgumentList @(150); [void]$strMsg.Append("`r`n$('*' * 100)`r`n*$(' ' * 98)*`r`n"); [void]$strMsg.Append("*$(' ' * 33)SnsPsModule - PowerShell Module$(' ' * 34)*`r`n"); [void]$strMsg.Append("*$(' ' * 37)ModuleVersion - 1.0.1.1$(' ' * 38)*`r`n*$(' ' * 98)*`r`n"); [void]$strMsg.Append("*$(' ' * 9)AUTHOR: Svetoslav Nedyalkov Savov$(' ' * 53)*`r`n"); [void]$strMsg.Append("*$(' ' * 9)THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK$(' ' * 9)*`r`n"); [void]$strMsg.Append("*$(' ' * 9)OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.$(' ' * 15)*`r`n"); [void]$strMsg.Append("*$(' ' * 98)*`r`n$('*' * 100)`r`n`r`n"); Write-Host "$($strMsg.ToString())" -ForegroundColor 'Green'; Remove-Variable -Force -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strMsg"; } #================================================================================== #endregion Modify Host Appearance #================================================================================== #================================================================================== #region Helper Comands #================================================================================== ##### Export-SnsCredentialFileAsService =========================================== Function Export-SnsCredentialFileAsService () { <# .SYNOPSIS This CmdLet Creates Encrypted Password File .DESCRIPTION This CmdLet Creates Encrypted Password File The CmdLet Is Intended To Prepare Credential File Of Other Accounts When It Is Run As A Service While The Password Is Encrypted Securely And Cannot Be Decrypted By Other Accounts Than The One Who Encrypted It, The Process For Creation Of The Encrypted File Is Not The Most Secure One This CmdLet Is Used Mainly When Automatic Scripts Will Need Other Credentials And Those Scripts Will Be Executed With Service Accounts That Have Interactive Logon Denied Active Logon Denied Makes Impossible The Usage Of The Export-SnsCredentialFile CmdLet Who Require The Credentials Interactively And The Password Is Not Provided In Clear Text The Produced Credential File Contains Information About When It Was Created And Who Created It WARNING: The Produced Encrypted Credential Files Can Be Decrypted Only Within The Security Context In Which They Are Created And Only On The Machine They Are Created With Other Words Only The Person Who Have Created A File Can Decrypt It On The Same Machine Only. WARNING: In Order To Create The Encrypted Password File This Function Will Require The Password To Be Provided As Clear Text To CmdLet Parameter There Is Increased Risk The Password To Be Intercepted In The Process Of Providing It To The CmdLet. .PARAMETER UserName Specifies The UserName Of The Credentials Which Have To Be Encrypted. Parameter Set: N/A Parameter Alias: N/A Parameter Validation: N/A .PARAMETER Password Specifies The Password Of The Credentials Which Have To Be Encrypted. Parameter Set: N/A Parameter Alias: N/A Parameter Validation: N/A .PARAMETER FolderPath Specifies The Full Absolute UNC Folder Path Where The Credential File Have To Be Exported. Parameter Set: N/A Parameter Alias: N/A Parameter Validation: Folder Existence Validation .INPUTS The CmdLet Does Not Support Pipeline Input. .OUTPUTS Encrypted Secure Password File. .NOTES AUTHOR: Svetoslav Nedyalkov Savov THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER. .EXAMPLE Export-SnsCredentialFileAsService -UserName 'Contoso\User01' -Password 'Pa$$w0rd' -FolderPath 'C:'; .LINK svesavov / SnsPsModule - https://github.com/svesavov/SnsPsModule .LINK Svetoslav Savov on LinkedIn - https://www.linkedin.com/in/svetoslavsavov #> [CmdletBinding(PositionalBinding = $false)] Param ( [Parameter(Mandatory = $true, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateNotNullOrEmpty()][System.String]$UserName, [Parameter(Mandatory = $true, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateNotNullOrEmpty()][System.String]$Password, [Parameter(Mandatory = $true, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateScript({("$($_)".Contains("\"))})] [ValidateScript({[System.IO.Directory]::Exists("$($_)")})] [System.String]$FolderPath ) ##### Override The Begin Method Begin { Write-Debug "Override Begin Method"; ##### Initialize The Variables [System.Security.SecureString]$strSecurePass = $null; [System.String]$strEncryptedPass = ""; [System.String]$strPath = ""; ##### Enumerate The Secure Password String Write-Debug "Enumerate The Secure Password String"; [System.Security.SecureString]$strSecurePass = $null; [System.Security.SecureString]$strSecurePass = "$($Password)" | ConvertTo-SecureString -AsPlainText:$true -Force:$true -Debug:$false -Verbose:$false; ##### Convert The Secure Password String To Encrypted Password String Write-Debug "Convert The Secure Password String To Encrypted Password String"; [System.String]$strEncryptedPass = ""; [System.String]$strEncryptedPass = $strSecurePass | ConvertFrom-SecureString -Verbose:$false -Debug:$false; ##### Enumerate The Encrypted File Path Write-Debug "Enumerate The Encrypted File Path"; [System.String]$strPath = ""; [System.String]$strPath = "$($FolderPath.Trim(""\""))\$(""$($UserName)"".Replace(""\"",""@@"")).ini"; If ([SnsPsModule.SnsCredentialFile]::Export("$($strEncryptedPass)", "$($strPath)")) { ##### Create The Confirmation File Write-Debug "Create The Confirmation File"; [System.String]$strPath = ""; [System.String]$strPath = "$($FolderPath.Trim('\'))\!!! Password_Export_OK !!!.txt"; "$([System.DateTime]::Now.ToString(""yyyy-MM-dd HH:mm:ss""))" | ` Set-Content -Path "$($strPath)" -Encoding 'Unicode' -Force:$true -Confirm:$false -PassThru:$false -Debug:$false -Verbose:$false; ##### } ##### Reset The Variables Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strPath"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strEncryptedPass"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strSecurePass"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "FolderPath"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "Password"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "UserName"; $PSCmdlet.MyInvocation.BoundParameters.Keys | ForEach { Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "$($_)"; } [System.GC]::Collect(); Exit 0; Stop-Process -Id $pid -Force:$true -Confirm:$false -Verbose:$false -Debug:$false; } } ##### Get-SnsNearestAliveCasServerHelper ========================================== Function Get-SnsNearestAliveCasServerHelper () { <# .SYNOPSIS Enumerates The Nearest Up And Running Exchange CAS Server. .DESCRIPTION Enumerates The Nearest Up And Running Exchange CAS Server. This Is A Helper Function And Won't Be Exported / Available To The User. .INPUTS Pipeline Input None .OUTPUTS [System.String] Which The FQDN Of The Enumerated CAS Server. .NOTES AUTHOR: Svetoslav Nedyalkov Savov THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER. .EXAMPLE [System.String]$HostName = Get-SnsNearestAliveCasServerHelper -Verbose:$bolVerbose -Debug:$false; .LINK svesavov / SnsPsModule - https://github.com/svesavov/SnsPsModule .LINK Svetoslav Savov on LinkedIn - https://www.linkedin.com/in/svetoslavsavov #> [CmdletBinding()] Param () ##### Override The Begin Method Begin { Write-Debug "Override Begin Method"; ##### Verify Whether The CmdLet Is Executed In Active Directory Domain ##### On Premises Exchange Can Live Only In Active Directory Environment Write-Debug "Verify Whether The CmdLet Is Executed In Active Directory Domain"; [System.String]$strHostName = ""; If (-not -not "$(([ADSI]"""").distinguishedName)") { ##### Generate The Ping Objects Write-Debug "Generate The Ping Objects"; [System.Net.NetworkInformation.Ping]$objPing = $null; [System.Net.NetworkInformation.Ping]$objPing = New-Object -TypeName 'System.Net.NetworkInformation.Ping' -Verbose:$false -Debug:$false; ##### Retrieve CAS Servers HostName From AD Write-Debug "Retrieve CAS Servers HostName From AD"; [System.Array]$arrCasSrvs = @(); [System.Array]$arrCasSrvs = Search-SnsAdObject -DomainController "$(([ADSI]""LDAP://RootDSE"").dnsHostName)" -ReturnProperties @("serviceDNSName") ` -LdapQuery "(&(objectClass=serviceConnectionPoint)(objectCategory=CN=Service-Connection-Point,$(([ADSI]""LDAP://RootDSE"").schemaNamingContext))(serviceClassName=ms-Exchange-AutoDiscover-Service))" ` -SearchRoot "$(([ADSI]""LDAP://RootDSE"").configurationNamingContext)" -Verbose:$false -Debug:$false; ##### ##### Ping All CAS Servers And Take The Nearest One Write-Debug "Ping All CAS Servers And Take The Nearest One"; [System.String]$strHostName = $arrCasSrvs | Select-Object -Verbose:$false -Debug:$false ` -Property @{ "n" = "Fqdn"; "e" = { "$($_.Properties.servicednsname).$(""$($_.Properties.adspath)"".Substring(""$($_.Properties.adspath)"".IndexOf(""DC="")).Replace(""DC="", """").Replace("","", "".""))" } } | ` Select-Object -Verbose:$false -Debug:$false ` -Property @( "Fqdn", @{ "n" = "TTL"; "e" = { [System.Int32]"$($objPing.Send(""$($_.Fqdn)"", 1000, [System.Text.Encoding]::ASCII.GetBytes(""S"")).Options.Ttl)" } } ) | ` Sort-Object -Property @( "Ttl", "Fqdn" ) -Descending:$true -Verbose:$false -Debug:$false | ` Select-Object -First 1 -Verbose:$false -Debug:$false | ` Select-Object -ExpandProperty "Fqdn" -Verbose:$false -Debug:$false; ##### Write-Verbose "Dynamically Enumerated CAS Server: ""$($strHostName)"""; ##### Reset The Variables Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "arrCasSrvs"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "objPing"; } Return $strHostName; } } ##### Import-SnsModuleHelper ====================================================== Function Import-SnsModuleHelper () { <# .SYNOPSIS Imports The Specified PowerShell Module Into The PowerShell Session. .DESCRIPTION Imports The Specified PowerShell Module Into The PowerShell Session. This Is A Helper Function And Won't Be Exported / Available To The User. .PARAMETER Module Specifies The Module Name Or Module Path. Parameter Alias: N/A Parameter Validation: N/A .PARAMETER EventSource Specifies The Application Log Event Source To Be Used For The Error Event Logging. Parameter Alias: N/A Parameter Validation: N/A .INPUTS Pipeline Input None .OUTPUTS [System.Boolean] Which Indicates Whether The Import Failed. .NOTES AUTHOR: Svetoslav Nedyalkov Savov THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER. .EXAMPLE If (Import-SnsModuleHelper -Module "$($ModuleName)" -EventSource "$($EventSource)") { Return; } .LINK svesavov / SnsPsModule - https://github.com/svesavov/SnsPsModule .LINK Svetoslav Savov on LinkedIn - https://www.linkedin.com/in/svetoslavsavov #> [CmdletBinding(PositionalBinding = $false)] Param ( [Parameter(Mandatory = $true, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateNotNullOrEmpty()][System.String]$Module, [Parameter(Mandatory = $true, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [AllowNull()][AllowEmptyString()][System.String]$EventSource = "" ) ##### Override The Begin Method Begin { Write-Debug "Override Begin Method"; ##### Enumerate The Module Name Write-Debug "Enumerate The Module Name"; [System.String]$strModuleName = "$(""$($Module)"".Split(""\"")[-1].Replace("".psd1"", """").Replace("".psm1"", """").Replace("".dll"", """"))"; ##### Verify AzureADPreview PowerShell Module Existence Write-Debug "Verify $($strModuleName) PowerShell Module Existence"; If (-not (Get-Module -Name "$($Module)" -ListAvailable:$true -Verbose:$false -Debug:$false)) { [System.String]$strEventMessage = "PowerShell Module $($strModuleName) Is Not Installed."; Log-SnsEventLogMessageHelper -Message "$($strEventMessage)" -EventLogEntryType "Error" -EventSource "$($EventSource)" -Verbose:$false -Debug:$false; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strEventMessage"; Return $true; } ##### Load AzureADPreview PowerShell Module Write-Debug "Load $($strModuleName) PowerShell Module"; If ((-not (Get-Module -Name "$($strModuleName)" -Verbose:$false -Debug:$false)) -and (-not -not (Get-Module -Name "$($Module)" -ListAvailable:$true -Verbose:$false -Debug:$false))) { Write-Host "Import $($strModuleName) Module" -ForegroundColor "Green"; [System.String]$strVerbosePreference = "$($VerbosePreference)"; $VerbosePreference = "SilentlyContinue"; Import-Module -Name "$($Module)" -Global:$true -Force:$true -Verbose:$false -Debug:$false; $VerbosePreference = "$($strVerbosePreference)"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strVerbosePreference"; } ##### Verify AzureADPreview PowerShell Module Load Write-Debug "Verify $($strModuleName) PowerShell Module Load"; If (-not (Get-Module -Name "$($strModuleName)" -Verbose:$false -Debug:$false)) { [System.String]$strEventMessage = "Failed To Load PowerShell Module $($strModuleName)"; Log-SnsEventLogMessageHelper -Message "$($strEventMessage)" -EventLogEntryType "Error" -EventSource "$($EventSource)" -Verbose:$false -Debug:$false; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strEventMessage"; Return $true; } ##### Reset The Variables Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strModuleName"; $PSCmdlet.MyInvocation.BoundParameters.Keys | ForEach { Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "$($_)"; } } ##### Override The End Method End { Write-Debug "Override End Method"; Write-Verbose ""; ##### Pass The Output Object To The Pipeline Write-Debug "Pass Output Object To The Pipeline"; Return $false; } } ##### Log-SnsEventLogMessageHelper ================================================ Function Log-SnsEventLogMessageHelper () { <# .SYNOPSIS This Helper CmdLet Simplifies Logging Of Events In The Console And Windows Event Viewer Application Log. .DESCRIPTION This Helper CmdLet Simplifies Logging Of Events In The Console And Windows Event Viewer Application Log. For Logging Into Event Viewer Is Required To Be Specified Existing EventSource. .PARAMETER Message Specifies The Event Message. Parameter Set: N/A Parameter Alias: N/A Parameter Validation: Using Existence Validation And Syntax Validation. .PARAMETER EventLogEntryType Specifies The Event Type / Severity. Parameter Set: N/A Parameter Alias: N/A Parameter Validation: Using ValidateSet Validation .PARAMETER EventSource Specifies The EventSource. Parameter Set: N/A Parameter Alias: N/A Parameter Validation: N/A .PARAMETER TerminatingError Specifies Whether An Error Would Be Terminating One And Exist The PowerShell Session With Code 1. Parameter Set: N/A Parameter Alias: N/A Parameter Validation: N/A .INPUTS The CmdLet Does Not Support Pipeline Input. .OUTPUTS The CmdLet Does Not Have Output. .NOTES AUTHOR: Svetoslav Nedyalkov Savov THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER. .EXAMPLE Log-SnsEventLogMessageHelper -Message "Test Message" -EventLogEntryType "Error" -EventSource "SnsPsModule"; .LINK svesavov / SnsPsModule - https://github.com/svesavov/SnsPsModule .LINK Svetoslav Savov on LinkedIn - https://www.linkedin.com/in/svetoslavsavov #> [CmdletBinding(PositionalBinding = $false)] Param( [Parameter(Mandatory = $true, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateNotNullOrEmpty()][System.String]$Message, [Parameter(Mandatory = $true, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateSet("Error", "Warning", "Information")] [ValidateNotNullOrEmpty()][System.String]$EventLogEntryType, [Parameter(Mandatory = $true, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [AllowNull()][AllowEmptyString()][System.String]$EventSource, [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [Switch]$TerminatingError = $true ) ##### Override The Begin Method Begin { Write-Debug "Override Begin Method"; ##### Verify The EventLogEntryType Write-Debug "Verify The EventLogEntryType"; Switch ("$($EventLogEntryType)") { "Error" { Write-Error "$($Message)" -ErrorAction "Continue"; Break; } "Warning" { Write-Warning "$($Message)" -WarningAction "Continue"; Break; } "Information" { Write-Host "$($Message)" -ForegroundColor "Green"; Break; } default { Write-Error "Unknown EventLogEntryType: ""$($EventLogEntryType)""." -ErrorAction "Stop"; } } ##### Verify Whether The EventSource Exists Write-Debug "Verify Whether The EventSource Exists"; If ((-not -not "$($EventSource)") -and [System.Diagnostics.EventLog]::SourceExists("$($EventSource)")) { [SnsPsModule.SnsEventLog]::WriteSnsEventLog("$($EventSource)", "$($Message)", "$($EventLogEntryType)", 9998); If (("$($EventLogEntryType)" -eq "Error") -and $TerminatingError.IsPresent) { Start-Sleep -Seconds 60 -Verbose:$false -Debug:$false; Exit 1; Stop-Process -Id $pid -Force:$true -Confirm:$false -Verbose:$false -Debug:$false; } } $PSCmdlet.MyInvocation.BoundParameters.Keys | ForEach { Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "$($_)"; } } } ##### New-SnsExoSessionInteractiveHelper ========================================== Function New-SnsExoSessionInteractiveHelper () { <# .SYNOPSIS This CmdLet Establish Interactive Remote PowerShell Session To ExchangeOnline Using ExchangeOnlineManagement PowerShell Module. .DESCRIPTION This CmdLet Establish Interactive Remote PowerShell Session To ExchangeOnline Using ExchangeOnlineManagement PowerShell Module. This Is A Helper Function Not Exported To The End User Session. .PARAMETER Prefix Specifies The Prefix For The CmdLets In This Session. Parameter Set: N/A Parameter Alias: N/A Parameter Validation: N/A .PARAMETER ProxyAccessType Specifies The ProxyAccessType. The Best Practice Require Direct Internet Access To Office 365. Parameter Set: N/A Parameter Alias: N/A Parameter Validation: N/A .PARAMETER Attempts Specifies The Number Of Attempts That Have To Be Made To Establish The Remote Session. Parameter Set: N/A Parameter Alias: N/A Parameter Validation: N/A .INPUTS Pipeline Input None .OUTPUTS [System.Management.Automation.PSModuleInfo] Which Contains The Imported Via The New PSSession Module. .NOTES AUTHOR: Svetoslav Nedyalkov Savov THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER. .EXAMPLE [System.Management.Automation.PSModuleInfo]$objSesModule = New-SnsExoSessionInteractiveHelper ` -Prefix "$($Prefix)" -Attempts $Attempts -ProxyAccessType "$($ProxyAccessType)"; .LINK svesavov / SnsPsModule - https://github.com/svesavov/SnsPsModule .LINK Svetoslav Savov on LinkedIn - https://www.linkedin.com/in/svetoslavsavov #> [CmdletBinding(PositionalBinding = $false)] Param ( [Parameter(Mandatory = $true, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [System.String]$Prefix, [Parameter(Mandatory = $true, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [System.String]$ProxyAccessType, [Parameter(Mandatory = $true, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [System.Int32]$Attempts ) ##### Override The Begin Method Begin { Write-Debug "Override Begin Method"; ##### Initialize The Variables [System.Array]$arrTmp = @(); [System.Collections.Specialized.OrderedDictionary]$hshSplat = [Ordered]@{}; [System.Int32]$intI = 0; [System.Management.Automation.PSModuleInfo[]]$arrSesModule = @(); [System.Management.Automation.PSModuleInfo]$objSesModule = $null; ##### Get Any Previous Temporary Modules Write-Debug "Get Any Previous Temporary Modules"; [System.String[]]$arrTmp = @(); [System.String[]]$arrTmp = Get-Module -Verbose:$false -Debug:$false | ` Select-Object -Verbose:$false -Debug:$false -ExpandProperty "Name" | ` Where-Object -Verbose:$false -Debug:$false {"$($_)".StartsWith("tmp_")}; ##### ##### Generate The Connect-ExchangeOnline Splatting HashTable Write-Debug "Generate The Connect-ExchangeOnline Splatting HashTable"; $hshSplat.Add("PSSessionOption", $(New-PSSessionOption -Verbose:$false -Debug:$false -SkipRevocationCheck:$true -SkipCACheck:$true -SkipCNCheck:$true -ProxyAccessType "$($ProxyAccessType)")); $hshSplat.Add("ShowBanner", $false); $hshSplat.Add("Verbose", $false); $hshSplat.Add("Debug", $false); $hshSplat.Add("InformationAction", "SilentlyContinue"); $hshSplat.Add("ErrorAction", "SilentlyContinue"); $hshSplat.Add("WarningAction", "SilentlyContinue"); ##### Verify Whether Prefix Is Specified Write-Debug "Verify Whether Prefix Is Specified"; If (-not -not "$($Prefix)") { $hshSplat.Add("Prefix", "$($Prefix)"); } ##### Loop The Session Creation Write-Debug "Loop The Session Creation"; [System.Int32]$intI = 0; [System.Management.Automation.PSModuleInfo]$objSesModule = $null; Do { ##### Establish The ExchangeOnline Session Write-Verbose "Establish The ExchangeOnline Session"; Connect-ExchangeOnline @hshSplat; ##### Enumerate The Session Modules Write-Debug "Enumerate The Session Modules" [System.Management.Automation.PSModuleInfo[]]$arrSesModule = @(); [System.Management.Automation.PSModuleInfo[]]$arrSesModule = Get-Module -Verbose:$false -Debug:$false | ` Where-Object -Verbose:$false -Debug:$false {"$($_.Name)".StartsWith("tmp_")} | ` Where-Object -Verbose:$false -Debug:$false {$arrTmp -inotcontains "$($_.Name)"}; ##### ##### Verify Whether There Are No More Than One Module Enumerated Write-Debug "Verify Whether There Are No More Than One Module Enumerated"; If ($arrSesModule.Count -gt 1) { ##### Filter Out The Non Exchange Online Temp Modules [System.Management.Automation.PSModuleInfo[]]$arrSesModule = $arrSesModule | ` Where-Object -Verbose:$false -Debug:$false {$_.ExportedCommands.Keys -icontains "Get-$($Prefix)AcceptedDomain"}; ##### } ##### Assign The Newly Created Module To The Verification Object Write-Debug "Assign The Newly Created Module To The Verification Object"; [System.Management.Automation.PSModuleInfo]$objSesModule = $null; If ($arrSesModule.Count -gt 0) { [System.Management.Automation.PSModuleInfo]$objSesModule = $arrSesModule[0]; } ##### Process The Loop Variable And TimeOut Start-Sleep -Seconds $(2 * $intI) -Verbose:$false -Debug:$false; [System.Int32]$intI = $intI + 1; } While ((-not "$($objSesModule.Name)") -and ($intI -lt $Attempts)) ##### Reset The Variables Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "arrSesModule"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "intI"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "hshSplat"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "arrTmp"; $PSCmdlet.MyInvocation.BoundParameters.Keys | ForEach { Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "$($_)"; } Return $objSesModule; } } ##### New-SnsExoSessionMsModuleHelper ============================================= Function New-SnsExoSessionMsModuleHelper () { <# .SYNOPSIS This CmdLet Establish Remote PowerShell Session To ExchangeOnline Using Certificate Thumbprint And ExchangeOnlineManagement PowerShell Module. .DESCRIPTION This CmdLet Establish Remote PowerShell Session To ExchangeOnline Using Certificate Thumbprint And ExchangeOnlineManagement PowerShell Module. This Is A Helper Function Not Exported To The End User Session. .PARAMETER CertificateThumbprint Specifies The Thumbprint Of A Certificate Used To Authenticate As Azure ServicePrincipal. Parameter Set: N/A Parameter Alias: N/A Parameter Validation: N/A .PARAMETER ApplicationId Specifies The ApplicationId Of Azure ServicePrincipal. Parameter Set: N/A Parameter Alias: N/A Parameter Validation: N/A .PARAMETER Organization Specifies The Organization Name. Parameter Set: N/A Parameter Alias: N/A Parameter Validation: N/A .PARAMETER Prefix Specifies The Prefix For The CmdLets In This Session. Parameter Set: N/A Parameter Alias: N/A Parameter Validation: N/A .PARAMETER ProxyAccessType Specifies The ProxyAccessType. The Best Practice Require Direct Internet Access To Office 365. Parameter Set: N/A Parameter Alias: N/A Parameter Validation: N/A .PARAMETER Attempts Specifies The Number Of Attempts That Have To Be Made To Establish The Remote Session. Parameter Set: N/A Parameter Alias: N/A Parameter Validation: N/A .INPUTS Pipeline Input None .OUTPUTS [System.Management.Automation.PSModuleInfo] Which Contains The Imported Via The New PSSession Module. .NOTES AUTHOR: Svetoslav Nedyalkov Savov THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER. .EXAMPLE [System.Management.Automation.PSModuleInfo]$objSesModule = New-SnsExoSessionMsModuleHelper ` -Organization "contoso.onmicrosoft.com" -CertificateThumbprint "012THISISADEMOTHUMBPRINT" ` -ApplicationId "00000000-0000-0000-0000-000000000000" -Prefix "$($Prefix)" -Attempts $Attempts; .LINK svesavov / SnsPsModule - https://github.com/svesavov/SnsPsModule .LINK Svetoslav Savov on LinkedIn - https://www.linkedin.com/in/svetoslavsavov #> [CmdletBinding(PositionalBinding = $false)] Param ( [Parameter(Mandatory = $true, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [System.String]$CertificateThumbprint, [Parameter(Mandatory = $true, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [System.String]$ApplicationId, [Parameter(Mandatory = $true, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [System.String]$Organization, [Parameter(Mandatory = $true, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [System.String]$Prefix, [Parameter(Mandatory = $true, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [System.String]$ProxyAccessType, [Parameter(Mandatory = $true, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [System.Int32]$Attempts ) ##### Override The Begin Method Begin { Write-Debug "Override Begin Method"; ##### Initialize The Variables [System.Array]$arrTmp = @(); [System.Collections.Specialized.OrderedDictionary]$hshSplat = [Ordered]@{}; [System.Int32]$intI = 0; [System.Management.Automation.PSModuleInfo[]]$arrSesModule = @(); [System.Management.Automation.PSModuleInfo]$objSesModule = $null; ##### Get Any Previous Temporary Modules Write-Debug "Get Any Previous Temporary Modules"; [System.String[]]$arrTmp = @(); [System.String[]]$arrTmp = Get-Module -Verbose:$false -Debug:$false | ` Select-Object -Verbose:$false -Debug:$false -ExpandProperty "Name" | ` Where-Object -Verbose:$false -Debug:$false {"$($_)".StartsWith("tmp_")}; ##### ##### Generate The Connect-ExchangeOnline Splatting HashTable Write-Debug "Generate The Connect-ExchangeOnline Splatting HashTable"; [System.Collections.Specialized.OrderedDictionary]$hshSplat = [Ordered]@{}; $hshSplat.Add("CertificateThumbprint", "$($CertificateThumbprint)"); $hshSplat.Add("Organization", "$($Organization)"); $hshSplat.Add("AppId", "$($ApplicationId)"); $hshSplat.Add("PSSessionOption", $(New-PSSessionOption -Verbose:$false -Debug:$false -SkipRevocationCheck:$true -SkipCACheck:$true -SkipCNCheck:$true -ProxyAccessType "$($ProxyAccessType)")); $hshSplat.Add("ShowBanner", $false); $hshSplat.Add("Verbose", $false); $hshSplat.Add("Debug", $false); $hshSplat.Add("InformationAction", "SilentlyContinue"); $hshSplat.Add("ErrorAction", "SilentlyContinue"); $hshSplat.Add("WarningAction", "SilentlyContinue"); ##### Verify Whether Prefix Is Specified Write-Debug "Verify Whether Prefix Is Specified"; If (-not -not "$($Prefix)") { $hshSplat.Add("Prefix", "$($Prefix)"); } ##### Loop The Session Creation Write-Debug "Loop The Session Creation"; [System.Int32]$intI = 0; [System.Management.Automation.PSModuleInfo]$objSesModule = $null; Do { ##### Establish The ExchangeOnline Session Write-Verbose "Establish The ExchangeOnline Session"; Connect-ExchangeOnline @hshSplat; ##### Enumerate The Session Modules Write-Debug "Enumerate The Session Modules" [System.Management.Automation.PSModuleInfo[]]$arrSesModule = @(); [System.Management.Automation.PSModuleInfo[]]$arrSesModule = Get-Module -Verbose:$false -Debug:$false | ` Where-Object -Verbose:$false -Debug:$false {"$($_.Name)".StartsWith("tmp_")} | ` Where-Object -Verbose:$false -Debug:$false {$arrTmp -inotcontains "$($_.Name)"}; ##### ##### Verify Whether There Are No More Than One Module Enumerated Write-Debug "Verify Whether There Are No More Than One Module Enumerated"; If ($arrSesModule.Count -gt 1) { ##### Filter Out The Non Exchange Online Temp Modules [System.Management.Automation.PSModuleInfo[]]$arrSesModule = $arrSesModule | ` Where-Object -Verbose:$false -Debug:$false {$_.ExportedCommands.Keys -icontains "Get-$($Prefix)AcceptedDomain"}; ##### } ##### Assign The Newly Created Module To The Verification Object Write-Debug "Assign The Newly Created Module To The Verification Object"; [System.Management.Automation.PSModuleInfo]$objSesModule = $null; If ($arrSesModule.Count -gt 0) { [System.Management.Automation.PSModuleInfo]$objSesModule = $arrSesModule[0]; } ##### Process The Loop Variable And TimeOut Start-Sleep -Seconds $(2 * $intI) -Verbose:$false -Debug:$false; [System.Int32]$intI = $intI + 1; } While ((-not "$($objSesModule.Name)") -and ($intI -lt $Attempts)) ##### Reset The Variables Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "arrSesModule"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "intI"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "hshSplat"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "arrTmp"; $PSCmdlet.MyInvocation.BoundParameters.Keys | ForEach { Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "$($_)"; } Return $objSesModule; } } ##### New-SnsRemoteSessionNoModuleHelper ========================================== Function New-SnsRemoteSessionNoModuleHelper () { <# .SYNOPSIS This CmdLet Establish Remote PowerShell Session To ExchangeOnline Using Credential Object Without External PowerShell Modules. .DESCRIPTION This CmdLet Establish Remote PowerShell Session To ExchangeOnline Using Credential Object Without External PowerShell Modules. This Is A Helper Function Not Exported To The End User Session. .PARAMETER Credential Specifies [System.Management.Automation.PSCredential] Object. Parameter Set: N/A Parameter Alias: N/A Parameter Validation: N/A .PARAMETER Url Specifies The Destination URL For The Remote Required Remote Session. Parameter Set: N/A Parameter Alias: N/A Parameter Validation: N/A .PARAMETER Prefix Specifies The Prefix For The CmdLets In This Session. Parameter Set: N/A Parameter Alias: N/A Parameter Validation: N/A .PARAMETER ProxyAccessType Specifies The ProxyAccessType. The Best Practice Require Direct Internet Access To Office 365. Parameter Set: N/A Parameter Alias: N/A Parameter Validation: N/A .PARAMETER Attempts Specifies The Number Of Attempts That Have To Be Made To Establish The Remote Session. Parameter Set: N/A Parameter Alias: N/A Parameter Validation: N/A .PARAMETER EventSource Specifies The Application Log Event Source To Be Used For The Error Event Logging. Parameter Set: N/A Parameter Alias: N/A Parameter Validation: N/A .INPUTS Pipeline Input None .OUTPUTS [System.Management.Automation.PSModuleInfo] Which Contains The Imported Via The New PSSession Module. .NOTES AUTHOR: Svetoslav Nedyalkov Savov THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER. .EXAMPLE [System.Management.Automation.PSModuleInfo]$objSesModule = New-SnsRemoteSessionNoModuleHelper ` -Credential $objCredential -Url "$($Url)" -Prefix "$($Prefix)" ` -ProxyAccessType "$($ProxyAccessType)" -Attempts $Attempts -EventSource "$($EventSource)"; .LINK svesavov / SnsPsModule - https://github.com/svesavov/SnsPsModule .LINK Svetoslav Savov on LinkedIn - https://www.linkedin.com/in/svetoslavsavov #> [CmdletBinding(PositionalBinding = $false)] Param ( [Parameter(Mandatory = $true, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [System.Management.Automation.PSCredential]$Credential, [Parameter(Mandatory = $true, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [System.String]$Url, [Parameter(Mandatory = $true, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [AllowNull()][AllowEmptyString()][System.String]$Prefix, [Parameter(Mandatory = $true, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [System.String]$ProxyAccessType, [Parameter(Mandatory = $true, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [System.Int32]$Attempts, [Parameter(Mandatory = $true, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [AllowNull()][AllowEmptyString()][System.String]$EventSource ) ##### Override The Begin Method Begin { Write-Debug "Override Begin Method"; ##### Initialize The Variables [System.Management.Automation.Runspaces.PSSession]$objPsSession = $null; [System.Collections.Specialized.OrderedDictionary]$hshSplat = [Ordered]@{}; [System.Int32]$intI = 0; [System.Management.Automation.PSModuleInfo]$objSesModule = $null; [System.String]$strSessionName = "$(""$($Url)"".Split(""/"")[2].Replace(""ps.outlook.com"", ""ExchangeOnlineInternalSession""))"; #================================================================================== #region Create Remote PSSession Session #================================================================================== ##### Verify The Prerequisites For Remote PSSession Write-Debug "Verify The Prerequisites For Remote PSSession"; [System.Management.Automation.Runspaces.PSSession]$objPsSession = $null; If (-not -not "$($Credential.UserName)") { ##### Generate The New-PSSession Splatting HashTable Write-Debug "Generate The New-PSSession Splatting HashTable"; [System.Collections.Specialized.OrderedDictionary]$hshSplat = [Ordered]@{}; $hshSplat.Add("Name", "$($strSessionName)"); $hshSplat.Add("ConfigurationName", "Microsoft.Exchange"); $hshSplat.Add("ConnectionUri", "$($Url)"); $hshSplat.Add("Authentication", "Basic"); $hshSplat.Add("Credential", $Credential); $hshSplat.Add("AllowRedirection", $true); $hshSplat.Add("SessionOption", $(New-PSSessionOption -Verbose:$false -Debug:$false -SkipRevocationCheck:$true -SkipCACheck:$true -SkipCNCheck:$true -ProxyAccessType "$($ProxyAccessType)")); $hshSplat.Add("Verbose", $false); $hshSplat.Add("Debug", $false); $hshSplat.Add("ErrorAction", "SilentlyContinue"); $hshSplat.Add("WarningAction", "SilentlyContinue"); ##### Loop The Session Creation Write-Debug "Loop The Session Creation"; [System.Int32]$intI = 0; Do { ##### Establish The Remote PSSession Session Write-Verbose "Establish A Remote PSSession Session ""$($strSessionName)"""; [System.Management.Automation.Runspaces.PSSession]$objPsSession = $null; [System.Management.Automation.Runspaces.PSSession]$objPsSession = New-PSSession @hshSplat; ##### Process The Loop Variable And TimeOut Start-Sleep -Seconds $(2 * $intI) -Verbose:$false -Debug:$false; [System.Int32]$intI = $intI + 1; } While (("$($objPsSession.Name)" -ne "$($strSessionName)") -and ($intI -lt $Attempts)) } ##### Verify Session Creation Write-Debug "Verify Session Creation"; If (-not "$($objPsSession.Name)") { [System.String]$strEventMessage = "Failed To Establish PowerShell Session ""$($strSessionName)"""; Log-SnsEventLogMessageHelper -Message "$($strEventMessage)" -EventLogEntryType "Error" -EventSource "$($EventSource)" -Verbose:$false -Debug:$false; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strEventMessage"; Return; } #================================================================================== #endregion Create ExchangeOnline Session #================================================================================== #================================================================================== #region Import The Remote PSSession Session #================================================================================== ##### Verify Whether The Session Is Established Write-Debug "Verify Whether The Session Is Established"; [System.Management.Automation.PSModuleInfo]$objSesModule = $null; If (-not -not "$($objPsSession.Name)") { ##### Generate The Import-PSSession Splatting HashTable Write-Debug "Generate The Import-PSSession Splatting HashTable"; [System.Collections.Specialized.OrderedDictionary]$hshSplat = [Ordered]@{}; $hshSplat.Add("Session", $objPsSession); $hshSplat.Add("AllowClobber", $true); $hshSplat.Add("DisableNameChecking", $true); #$hshSplat.Add("Verbose", $false); ##### Dont Uncomment Shows A Lot Of Verbose Messages Even With False Value $hshSplat.Add("Debug", $false); $hshSplat.Add("ErrorAction", "SilentlyContinue"); $hshSplat.Add("WarningAction", "SilentlyContinue"); ##### Verify Whether Prefix Is Specified Write-Debug "Verify Whether Prefix Is Specified"; If (-not -not "$($Prefix)") { $hshSplat.Add("Prefix", "$($Prefix)"); } ##### Import The Remote PSSession Session Write-Verbose "Importing The Remote PSSession With Prefix ""$($Prefix)"""; $VerbosePreference = "SilentlyContinue"; [System.Management.Automation.PSModuleInfo]$objSesModule = $null; [System.Management.Automation.PSModuleInfo]$objSesModule = Import-Module -ModuleInfo ( Import-PSSession @hshSplat ) ` -Prefix "$($Prefix)" -Global:$true -DisableNameChecking:$true -Force:$true -PassThru:$true -Debug:$false; ##### } ##### Verify The PSSession Import Write-Debug "Verify The PSSession Import"; If (-not "$($objSesModule.Name)") { [System.String]$strEventMessage = "Failed To Import The Remote PSSession ""$($strSessionName)""."; Log-SnsEventLogMessageHelper -Message "$($strEventMessage)" -EventLogEntryType "Error" -EventSource "$($EventSource)" -Verbose:$false -Debug:$false; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strEventMessage"; Return; } #================================================================================== #endregion Import The Remote PSSession Session #================================================================================== ##### Reset The Variables Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "objPsSession"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "hshSplat"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "intI"; $PSCmdlet.MyInvocation.BoundParameters.Keys | ForEach { Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "$($_)"; } return $objSesModule; } } ##### Prepare-SnsHostForRemoteSessions ============================================ Function Prepare-SnsHostForRemoteSessions () { <# .SYNOPSIS Modifies The Host Machine Settings To Allow Remote PSSession To Office365. .DESCRIPTION Modifies The Host Machine Settings To Allow Remote PSSession To Office365. The CmdLet Performs The Following Actions In Sequence: -- Check WinRM Service Existence -- Check Whether WinRM Service Is Set As Automatic And Modify It If Needed. -- Check Whether WinRM Service Is Running And Start It If Needed. -- Check AllowBasic Registry Key Existence -- Check Whether AllowBasic Registry Key Have Value 1 And Modify It If Needed. This CmdLet Is Internal For The Module And Will Not Be Exported To The User. .PARAMETER EventSource Specifies The Application Log Event Source To Be Used For The Error Event Logging. Parameter Set: N/A Parameter Alias: N/A Parameter Validation: N/A .PARAMETER WhatIf Specifies To The CmdLet To Show All The Actions That Will Normally Do Without Doing Them. Lack Of Errors During WhatIf Execution Is Not Indicator That There Will Be No Any During Real Execution. Parameter Alias: N/A Parameter Validation: N/A .INPUTS Pipeline Input None .OUTPUTS [System.Boolean[]] Which Revert Information Whether All Required Settings Are Correct .NOTES AUTHOR: Svetoslav Nedyalkov Savov THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER. .EXAMPLE [System.Boolean[]]$bolSuccess = Prepare-SnsHostForRemoteSessions "$($EventSource)"; .LINK svesavov / SnsPsModule - https://github.com/svesavov/SnsPsModule .LINK Svetoslav Savov on LinkedIn - https://www.linkedin.com/in/svetoslavsavov #> [CmdletBinding(PositionalBinding = $false, SupportsShouldProcess = $true, ConfirmImpact = "Low")] Param ( [Parameter(Mandatory = $false, Position = 0, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [AllowNull()][AllowEmptyString()][System.String]$EventSource ) ##### Override The Begin Method Begin { Write-Debug "Override Begin Method"; Write-Verbose ""; Write-Verbose "Prepare-SnsHostForRemoteSessions"; #Write-Verbose "ParameterSetName: $($PSCmdlet.ParameterSetName)`r`n"; ##### Initialize The Variables [System.Array]$arrSrvc = @(); [System.Array]$arrRgstr = @(); #================================================================================== #region Set WinRM Service To Automatic And Start It #================================================================================== ##### Get WinRM Service Write-Debug "Get WinRM Service"; [System.Array]$arrSrvc = @(); [System.Array]$arrSrvc = Get-Service -Name "WinRM" -Verbose:$false -Debug:$false -ErrorAction "SilentlyContinue"; ##### Verify WinRM Service Existence Write-Debug "Verify WinRM Service Existence"; If (($arrSrvc.Count -eq 1) -and ("$($arrSrvc[0].Name)" -eq "WinRM")) { #================================================================================== #region Set WinRM Service To Automatic #================================================================================== ##### Verify Whether WinRM Service Is Automatic Write-Debug "Verify Whether WinRM Service Is Automatic"; If ("$($arrSrvc[0].StartType)" -ne "Automatic") { ##### Check If The CmdLet Should Process If ($PSCmdlet.ShouldProcess("Set-Service WinRM To Automatic")) { ##### Set WinRM Service To Automatic Write-Debug "Set-Service WinRM To Automatic"; Set-Service -Name "WinRM" -StartupType "Automatic" -Confirm:$false -Verbose:$false -Debug:$false | Out-Null; Start-Sleep -Seconds 5 -Verbose:$false -Debug:$false; ##### Get WinRM Service Write-Debug "Get WinRM Service"; [System.Array]$arrSrvc = @(); [System.Array]$arrSrvc = Get-Service -Name "WinRM" -Verbose:$false -Debug:$false -ErrorAction "SilentlyContinue"; ##### Verify Whether WinRM Is Properly Reconfigured Write-Debug "Verify Whether WinRM Is Properly Reconfigured"; If ("$($arrSrvc[0].StartType)" -ne "Automatic") { [System.String]$strEventMessage = "Failed To Set WinRM To Automatic"; Log-SnsEventLogMessageHelper -Message "$($strEventMessage)" -EventLogEntryType "Error" -EventSource "$($EventSource)" -Verbose:$false -Debug:$false; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strEventMessage"; Return $false; } } } #================================================================================== #endregion Set WinRM Service To Automatic #================================================================================== #================================================================================== #region Start WinRM Service #================================================================================== ##### Verify Whether WinRM Service Is Running Write-Debug "Verify Whether WinRM Service Is Running"; If ("$($arrSrvc[0].Status)" -ne "Running") { ##### Check If The CmdLet Should Process If ($PSCmdlet.ShouldProcess("Start-Service WinRM")) { ##### Start WinRM Service Write-Debug "Start WinRM Service"; Start-Service -Name "WinRM" -Confirm:$false -Verbose:$false -Debug:$false | Out-Null; Start-Sleep -Seconds 5 -Verbose:$false -Debug:$false; ##### Get WinRM Service Write-Debug "Get WinRM Service"; [System.Array]$arrSrvc = @(); [System.Array]$arrSrvc = Get-Service -Name "WinRM" -Verbose:$false -Debug:$false -ErrorAction "SilentlyContinue"; ##### Verify Whether WinRM Is Properly Started Write-Debug "Verify Whether WinRM Is Properly Started"; If ("$($arrSrvc[0].Status)" -ne "Running") { [System.String]$strEventMessage = "Failed To Start WinRM Service"; Log-SnsEventLogMessageHelper -Message "$($strEventMessage)" -EventLogEntryType "Error" -EventSource "$($EventSource)" -Verbose:$false -Debug:$false; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strEventMessage"; Return $false; } } } #================================================================================== #endregion Start WinRM Service #================================================================================== } #================================================================================== #endregion Set WinRM Service To Automatic And Start It #================================================================================== #================================================================================== #region Allow Basic Authnetication #================================================================================== ##### Get AllowBasic Registry Key Value Write-Debug "Get AllowBasic Registry Key Value"; [System.Array]$arrRgstr = @(); If (Test-Path -Path "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WinRM\Client") { Try { ##### Even With SilentlyContinue The .NET Exceptions Can Prevent From Continue [System.Array]$arrRgstr = Set-SnsRegistry -RegistryPath "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WinRM\Client" -RegistryName "AllowBasic" -RegistryType "DWord" ` -RegistryValue "1" -PassThru:$true -WhatIf:$true -Verbose:$false -Debug:$false -ErrorAction "SilentlyContinue" -WarningAction "SilentlyContinue"; ##### } Catch {} ##### Verify Whether AllowBasic Registry Key Value Exists And The Value Is Correct Write-Debug "Verify Whether AllowBasic Registry Key Value Exists And The Value Is Correct"; If (("$($arrRgstr[0].RegistryName)" -eq "AllowBasic") -and (-not $arrRgstr[0].ValueCorrect)) { ##### Check If The CmdLet Should Process If ($PSCmdlet.ShouldProcess("Enable WinRM Basic Authentication")) { ##### Enable WinRM Basic Authentication Write-Debug "Enable WinRM Basic Authentication"; [System.Array]$arrRgstr = @(); [System.Array]$arrRgstr = Set-SnsRegistry -RegistryPath "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WinRM\Client" -RegistryName "AllowBasic" -RegistryType "DWord" ` -RegistryValue "1" -PassThru:$true -WhatIf:$false -Confirm:$false -Force:$true -Verbose:$false -Debug:$false; ##### ##### Verify WinRM Basic Authentication Enablement Write-Debug "Verify WinRM Basic Authentication Enablement"; If (-not $arrRgstr[0].ValueCorrect) { [System.String]$strEventMessage = "Failed To Enable WinRM Basic Authentication"; Log-SnsEventLogMessageHelper -Message "$($strEventMessage)" -EventLogEntryType "Error" -EventSource "$($EventSource)" -Verbose:$false -Debug:$false; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strEventMessage"; Return $false; } } } } #================================================================================== #endregion Allow Basic Authnetication #================================================================================== ##### Pass The Output Object To The Pipeline Write-Debug "Pass Output Object To The Pipeline"; $PSCmdlet.WriteObject($true); ##### Reset The Variables Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "arrRgstr"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "arrSrvc"; $PSCmdlet.MyInvocation.BoundParameters.Keys | ForEach { Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "$($_)"; } } } ##### Remove-SnsPreviousSessionsHelper ============================================ Function Remove-SnsPreviousSessionsHelper () { <# .SYNOPSIS Removes Any Established Remote PsSessions That Match The Specified Filter Along With Associated To Them Temporary PowerShell Modules. .DESCRIPTION Removes Any Established Remote PsSessions That Match The Specified Filter Along With Associated To Them Temporary PowerShell Modules. This Is Internal Helper CmdLet Not Available To The User. .PARAMETER Filter Specifies The Filter Which Will Be Applied Against The Session Name. Parameter Set: N/A Parameter Alias: N/A Parameter Validation: N/A .PARAMETER ScrBlock Specifies The Verification ScriptBlock Used To Verify Whether The Remote PSSession Is Established. Parameter Set: N/A Parameter Alias: N/A Parameter Validation: N/A .INPUTS Pipeline Input None .OUTPUTS No Output .NOTES AUTHOR: Svetoslav Nedyalkov Savov THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER. .EXAMPLE Remove-SnsPreviousSessionsHelper """$($_.Name)"" -like ""ExchangeOnlineInternalSession*"""; .LINK svesavov / SnsPsModule - https://github.com/svesavov/SnsPsModule .LINK Svetoslav Savov on LinkedIn - https://www.linkedin.com/in/svetoslavsavov #> [CmdletBinding(PositionalBinding = $false)] Param ( [Parameter(Mandatory = $true, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateNotNullOrEmpty()][System.String]$Filter, [Parameter(Mandatory = $true, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [System.Management.Automation.ScriptBlock]$ScrBlock ) ##### Override The Begin Method Begin { Write-Debug "Override Begin Method"; ##### Initialize The Variables [System.Array]$arrTmp = @(); [System.String[]]$arrModules = @(); [System.Int32]$intI = 0; ##### Enumerate Any Previous Sessions Write-Debug "Enumerate Any Previous Sessions"; [System.Array]$arrTmp = @(); [System.Array]$arrTmp = Get-PSSession -Verbose:$false -Debug:$false | Where-Object -Verbose:$false -Debug:$false -Property "Name" -like "$($Filter)*" | ` Select-Object -Verbose:$false -Debug:$false -Property @("Name", "CurrentModuleName"); ##### ##### Verify Any Previous Sessions Existence Write-Debug "Verify Any Previous Sessions Existence"; If ($arrTmp.Count -gt 0) { ##### Loop All Previous Sessions [System.Int32]$intI = 0; For ([System.Int32]$intI = 0; $intI -lt $arrTmp.Count; $intI++) { ##### Disconnect The Previous Sessions Write-Verbose "Removing Previous Session: ""$($arrTmp[$intI].Name)"""; Remove-PSSession -Name "$($arrTmp[$intI].Name)" -Confirm:$false -WhatIf:$false -Verbose:$false -Debug:$false; ##### Verify Whether The Temporary Module Is Enumerated Write-Debug "Verify Whether The Temporary Module Is Enumerated"; If (-not "$($arrTmp[$intI].CurrentModuleName)") { [System.String[]]$arrModules = Get-Module -Verbose:$false -Debug:$false | ` Where-Object -Verbose:$false -Debug:$false {"$($_.Name)".StartsWith("tmp_")} | ` Where-Object -Verbose:$false -Debug:$false {$_.ExportedCommands.Keys -icontains "$($scrBlock)"} | ` Select-Object -Verbose:$false -Debug:$false -ExpandProperty "Name"; ##### } Else { [System.String[]]$arrModules = @("$($arrTmp[$intI].CurrentModuleName)"); } ##### Verify Whether There Are Temporary Modules For Removal Write-Debug "Verify Whether There Are Temporary Modules For Removal"; If ($arrModules.Count -gt 0) { ##### Loop All Previous Sessions [System.Int32]$intIn = 0; For ([System.Int32]$intIn = 0; $intIn -lt $arrModules.Count; $intIn++) { ##### Verify Whether The Temporary Module Is Enumerated Write-Debug "Verify Whether The Temporary Module Is Enumerated"; If (-not -not "$($arrModules[$intIn])") { ##### Remove The Previous Temporary Module Write-Verbose "Removing Previous Temporary Module: ""$($arrModules[$intIn])"""; Remove-Module -Name "$($arrModules[$intIn])" -Force:$true -Confirm:$false -WhatIf:$false -Verbose:$false -Debug:$false; } } } } } ##### Prepare The Host Machine For Remote PSSession Write-Debug "Prepare The Host Machine For Remote PSSession"; If (-not (Prepare-SnsHostForRemoteSessions "$($EventSource)" -Confirm:$false -WhatIf:$false -Verbose:$false -Debug:$false)) { Return; } ##### Reset The Variables Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "intI"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "arrTmp"; $PSCmdlet.MyInvocation.BoundParameters.Keys | ForEach { Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "$($_)"; } } } ##### Test-SnsCredentialHelper ==================================================== Function Test-SnsCredentialHelper () { <# .SYNOPSIS Verifies The Specified Credentials Against The Current Active Directory Domain. .DESCRIPTION Verifies The Specified Credentials Against The Current Active Directory Domain. This Is A Helper Function And Won't Be Exported / Available To The User. .INPUTS Pipeline Input None .OUTPUTS [System.Boolean] Which Indicates Whether The Specified Credentials Are Valid. .NOTES AUTHOR: Svetoslav Nedyalkov Savov THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER. .EXAMPLE [System.Boolean]$bolCheckCred = Test-SnsCredentialHelper -Credential $Credential -Verbose:$bolVerbose; .LINK svesavov / SnsPsModule - https://github.com/svesavov/SnsPsModule .LINK Svetoslav Savov on LinkedIn - https://www.linkedin.com/in/svetoslavsavov #> [CmdletBinding(PositionalBinding = $false)] Param ( [Parameter(Mandatory = $true, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateNotNullOrEmpty()][System.Management.Automation.PSCredential]$Credential ) ##### Override The Begin Method Begin { Write-Debug "Override Begin Method"; ##### Verify Whether The CmdLet Is Executed In Active Directory Domain ##### On Premises Exchange Can Live Only In Active Directory Environment Write-Debug "Verify Whether The CmdLet Is Executed In Active Directory Domain"; [System.String]$strDomainName = ""; [System.String]$strDomainDn = "$(([ADSI]"""").distinguishedName)"; If (-not -not "$($strDomainDn)") { [System.String]$strDomainName = "$((New-Object -TypeName ""System.DirectoryServices.DirectoryEntry"" -Verbose:$false -Debug:$false -ArgumentList ` @( ""LDAP://$($strDomainDn)"", ""$($objCredential.UserName)"", ""$($objCredential.GetNetworkCredential().Password)"" )).name)"; ##### } ##### Reset The Variables Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strDomainDn"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "Credential"; Return (-not -not "$($strDomainName)"); } } #================================================================================== #endregion Helper Comands #================================================================================== #================================================================================== #region Commands #================================================================================== ##### Assert-SnsDirectAssignedLicense ============================================= Function Assert-SnsDirectAssignedLicense () { <# .SYNOPSIS Evaluates Whether Specified Licenses Is Assigned To The Specified User Directly Using PowerShell Or Admin Portal. .DESCRIPTION Evaluates Whether Specified Licenses Is Assigned To The Specified User Directly Using PowerShell Or Admin Portal. This CmdLet Search Among The Licenses Assigned To The User About The Specified License. Then Reads The GroupsAssigningLicense Property Of The Specified License. This License Property Normally Contains A Collection Of Object ID's Of The Objects That Have Assigned That License. In Case The License Is Assigned Directly Via PowerShell Or The Admin Portal, There Will Be The Users ObjectID (Not Administrators One) Or The Collection Will Be Empty. The Value Of The Property Can Neither Be Used For Identification Who Did Assigned The License, Nor For Troubleshooting, Because The Value There Is The Users Own ObjectID. The Collection Will Be Empty Whenever The Group Based License Feature Were Never Used In The Tenant. In Case The User Inherits Specified License From A Group Or Groups, The Collection Will Contain The ObjectID Of The Groups Which Assign The Specified License To The User. In That Way, The ObjectID's From The Collection Can Be Used For Troubleshooting. Note: A License Might Be Assigned Directly In Addition To Being Inherited. In Case A License Is Directly Assigned Will Be Wrong Assumption That It Is Not Inherited From A Group. Note: The CmdLet Does Not Perform Any Queries To Msol V1 Service As Long As All The Required Information Is Already Present In The Required Input. It Just Extracts The Information From There. .PARAMETER MsolUser Specifies MsolUser Object Which Have To Be Evaluated. Parameter Alias: N/A Parameter Validation: Yes Using Object TypeName Validation .PARAMETER SkuId Specifies The ID Of The License Which Have To Be Evaluated. Parameter Alias: N/A Parameter Validation: Yes, Using RegEx Validation .INPUTS The CmdLet Does Not Accept Pipeline Input. .OUTPUTS Pipeline Output [System.Boolean] Which Indicates Whether The Specified User Have The Specified License Directly Assigned. .NOTES AUTHOR: Svetoslav Nedyalkov Savov THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER. .EXAMPLE [System.Boolean]$bolGroupAssigned = Assert-SnsDirectAssignedLicense -MsolUser $objUser ` -SkuId "contoso:ENTERPRISEPACK"; .LINK svesavov / SnsPsModule - https://github.com/svesavov/SnsPsModule .LINK Svetoslav Savov on LinkedIn - https://www.linkedin.com/in/svetoslavsavov #> [CmdletBinding(PositionalBinding = $false)] Param ( [Parameter(Mandatory = $true, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [Alias("User")] [ValidateNotNullOrEmpty()] [ValidateScript({("$(($_ | Get-Member -Verbose:$false -Debug:$false )[0].TypeName)" -like "*Microsoft.Online.Administration.User")})] [System.Object]$MsolUser, [Parameter(Mandatory = $true, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateNotNullOrEmpty()] [ValidateScript({("$($_)" -match "^[a-z]+:[a-zA-Z0-9_]+$")})] [System.String]$SkuId ) ##### Override The Begin Method Begin { Write-Debug "Override Begin Method"; Write-Verbose ""; Write-Verbose "Assert-SnsDirectAssignedLicense"; #Write-Verbose "ParameterSetName: $($PSCmdlet.ParameterSetName)`r`n"; ##### Initialize New Measure Watch [System.Diagnostics.Stopwatch]$objCmdStopWatch = [System.Diagnostics.Stopwatch]::StartNew(); If ("$((Get-FileHash -Path ""$($PSCommandPath)"" -Algorithm ""SHA256"" -Verbose:$false -Debug:$false).Hash)" -ne "$($global:SnsModuleCfg.ModuleHash)") { Write-Warning "There Is New Version Of SnsPsModule Module Released. Please Restart The PowerShell Session." -WarningAction "Continue"; }; ##### Initialize The Variables [System.Int32]$intI = 0; [System.Int32]$intIn = 0; [System.Boolean]$bolDirect = $false; ##### Continue If The User Have Licenses Assigned Write-Debug "Continue If The User Have Licenses Assigned"; If ($MsolUser.Licenses.Count -gt 0) { ##### Process All Assigned To The User Licenses Write-Debug "Process All Assigned To The User Licenses"; [System.Int32]$intI = 0; For ([System.Int32]$intI = 0; $intI -lt $MsolUser.Licenses.Count; $intI++) { ##### We Look For The Specified License SKU In All Licenses Assigned To The User If ("$($MsolUser.Licenses[$intI].AccountSkuId)" -eq "$($SkuId)") { ##### GroupsAssigningLicense Property Contains A Collection Of IDs Of Objects Assigning The License ##### This Could Be A Group Object Or A User Object (Contrary To What The Name Suggests) ##### If The Collection Contains At Least One ID Not Matching The User ID This Means That The License Is Inherited From A Group. ##### Note: The License May Also Be Assigned Directly In Addition To Being Inherited ##### In Case In The Tenant Were Never Used Group Based Licensing The GroupsAssigningLicense Will Be Empty ##### Verify The Count Of The License Assigning Sources Write-Debug "Verify The Count Of The License Assigning Sources"; If ($MsolUser.Licenses[$intI].GroupsAssigningLicense.Count -gt 0) { ##### Process Each License Assigning Source As It Can Be A Collection Write-Debug "Process Each License Assigning Source"; [System.Int32]$intIn = 0; For ([System.Int32]$intIn = 0; $intIn -lt $MsolUser.Licenses[$intI].GroupsAssigningLicense.Count; $intIn++) { ##### Check If The Current Assignment Source Is The User Himself If ("$($MsolUser.Licenses[$intI].GroupsAssigningLicense[$intIn].Guid)" -eq "$($MsolUser.ObjectId)") { ##### This License Is Directly Assigned Write-Verbose "The License $($SkuId) Is Directly Assigned To The User"; $bolDirect = $true; } } } Else { ##### There Are No ObjectID In The Object Property ##### Which Means The Tenant Has Never GBL ##### Which MEans That The License Can Be Assigned Only Directly Write-Verbose "The License $($SkuId) Is Directly Assigned To The User"; [System.Boolean]$bolDirect = $true; } } } } ##### Pass The Output Object To The Pipeline Write-Debug "Pass Output Object To The Pipeline"; $PSCmdlet.WriteObject($bolDirect); ##### Reset The Variables Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "bolDirect"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "intIn"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "intI"; $PSCmdlet.MyInvocation.BoundParameters.Keys | ForEach { Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "$($_)"; } } ##### Override The End Method End { Write-Debug "Override End Method"; Write-Verbose ""; ##### Stop The StopWatch $objCmdStopWatch.Stop(); Write-Verbose "Command Elapsed: ""$($objCmdStopWatch.ElapsedMilliseconds)"" Milliseconds." ; Write-Verbose "End!"; } } ##### Assert-SnsGroupBasedLicense ================================================= Function Assert-SnsGroupBasedLicense () { <# .SYNOPSIS Evaluates Whether Specified Licenses Is Assigned To The Specified User Using AzureAD Group Based Licensing. .DESCRIPTION Evaluates Whether Specified Licenses Is Assigned To The Specified User Using AzureAD Group Based Licensing. This CmdLet Search Among The Licenses Assigned To The User About The Specified License. Then Reads The GroupsAssigningLicense Property Of The Specified License. This License Property Normally Contains A Collection Of Object ID's Of The Objects That Have Assigned That License. In Case The License Is Assigned Directly Via PowerShell Or The Admin Portal, There Will Be The Users ObjectID (Not Administrators One) Or The Collection Will Be Empty. The Value Of The Property Can Neither Be Used For Identification Who Did Assigned The License, Nor For Troubleshooting, Because The Value There Is The Users Own ObjectID. The Collection Will Be Empty Whenever The Group Based License Feature Were Never Used In The Tenant. In Case The User Inherits Specified License From A Group Or Groups, The Collection Will Contain The ObjectID Of The Groups Which Assign The Specified License To The User. In That Way, The ObjectID's From The Collection Can Be Used For Troubleshooting. For That Purpose, The CmdLet Have Switch Parameter PassThru, Which Is Used To Revert The ObjectID Of The Groups Assigning The Specified License To The Specified User. Note: A License Might Be Assigned Directly In Addition To Being Inherited. In Case A License Is Inherited Will Be Wrong Assumption That It Is Not Directly Assigned. Note: The CmdLet Does Not Perform Any Queries To Msol V1 Service As Long As All The Required Information Is Already Present In The Required Input. It Just Extracts The Information From There. .PARAMETER MsolUser Specifies MsolUser Object Which Have To Be Evaluated. Parameter Alias: N/A Parameter Validation: Yes Using Object TypeName Validation .PARAMETER SkuId Specifies The ID Of The License Which Have To Be Verified. Parameter Alias: N/A Parameter Validation: Yes, Using RegEx Validation .PARAMETER PassThru Specifies That The CmdLet Have To Revert The ObjectID's Of The Groups That The Specified User Inherits The Specified License From. Parameter Alias: N/A Parameter Validation: N/A .INPUTS The CmdLet Does Not Accept Pipeline Input. .OUTPUTS Pipeline Output [System.Boolean] Which Indicates Whether The Specified User Inherits The Specified License From Groups. .NOTES AUTHOR: Svetoslav Nedyalkov Savov THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER. .EXAMPLE [System.Boolean]$bolGroupAssigned = Assert-SnsGroupBasedLicense -MsolUser $objUser ` -SkuId "contoso:ENTERPRISEPACK"; .LINK svesavov / SnsPsModule - https://github.com/svesavov/SnsPsModule .LINK Svetoslav Savov on LinkedIn - https://www.linkedin.com/in/svetoslavsavov #> [CmdletBinding(PositionalBinding = $false)] Param ( [Parameter(Mandatory = $true, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [Alias("User")] [ValidateNotNullOrEmpty()] [ValidateScript({("$(($_ | Get-Member -Verbose:$false -Debug:$false )[0].TypeName)" -like "*Microsoft.Online.Administration.User")})] [System.Object]$MsolUser, [Parameter(Mandatory = $true, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateNotNullOrEmpty()] [ValidateScript({("$($_)" -match "^[a-z]+:[a-zA-Z0-9_]+$")})] [System.String]$SkuId, [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [Switch]$PassThru = $false ) ##### Override The Begin Method Begin { Write-Debug "Override Begin Method"; Write-Verbose ""; Write-Verbose "Assert-SnsGroupBasedLicense"; #Write-Verbose "ParameterSetName: $($PSCmdlet.ParameterSetName)`r`n"; ##### Initialize New Measure Watch [System.Diagnostics.Stopwatch]$objCmdStopWatch = [System.Diagnostics.Stopwatch]::StartNew(); If ("$((Get-FileHash -Path ""$($PSCommandPath)"" -Algorithm ""SHA256"" -Verbose:$false -Debug:$false).Hash)" -ne "$($global:SnsModuleCfg.ModuleHash)") { Write-Warning "There Is New Version Of SnsPsModule Module Released. Please Restart The PowerShell Session." -WarningAction "Continue"; }; ##### Initialize The Variables [System.Int32]$intI = 0; [System.Int32]$intIn = 0; [System.Boolean]$bolGbl = $false; ##### Continue If The User Have Licenses Assigned Write-Debug "Continue If The User Have Licenses Assigned"; If ($MsolUser.Licenses.Count -gt 0) { ##### Process All Assigned To The User Licenses Write-Debug "Process All Assigned To The User Licenses"; [System.Int32]$intI = 0; For ([System.Int32]$intI = 0; $intI -lt $MsolUser.Licenses.Count; $intI++) { ##### We Look For The Specified License SKU In All Licenses Assigned To The User If ("$($MsolUser.Licenses[$intI].AccountSkuId)" -like "$($SkuId)") { ##### GroupsAssigningLicense Property Contains A Collection Of IDs Of Objects Assigning The License ##### This Could Be A Group Object Or A User Object (Contrary To What The Name Suggests) ##### If The Collection Contains At Least One ID Not Matching The User ID This Means That The License Is Inherited From A Group. ##### Note: The License May Also Be Assigned Directly In Addition To Being Inherited ##### In Case In The Tenant Were Never Used Group Based Licensing The GroupsAssigningLicense Will Be Empty ##### Verify The Count Of The License Assigning Sources Write-Debug "Verify The Count Of The License Assigning Sources"; If ($MsolUser.Licenses[$intI].GroupsAssigningLicense.Count -gt 0) { ##### Process Each License Assigning Source As It Can Be A Collection Write-Debug "Process Each License Assigning Source"; [System.Int32]$intIn = 0; For ([System.Int32]$intIn = 0; $intIn -lt $MsolUser.Licenses[$intI].GroupsAssigningLicense.Count; $intIn++) { ##### Check If The Current Assignment Source Belongs To Object Different Than The User Himself If ("$($MsolUser.Licenses[$intI].GroupsAssigningLicense[$intIn].Guid)" -ne "$($MsolUser.ObjectId)") { ##### This License Is Group Inherited Write-Verbose "The License $($SkuId) Is Assigned To The User Via Group $($MsolUser.Licenses[$intI].GroupsAssigningLicense[$intIn].Guid)"; $bolGbl = $true; ##### Verify Whether Return Object Has Been Requested If ($PassThru.IsPresent) { ##### Pass The Output Object To The Pipeline Write-Debug "Pass Output Object To The Pipeline"; $PSCmdlet.WriteObject("$($MsolUser.Licenses[$intI].GroupsAssigningLicense[$intIn].Guid)"); } } } } } } } ##### Verify Whether Return Object Has Been Requested If (-not $PassThru.IsPresent) { ##### Pass The Output Object To The Pipeline Write-Debug "Pass Output Object To The Pipeline"; $PSCmdlet.WriteObject($bolGbl); } ##### Reset The Variables Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "bolGbl"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "intIn"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "intI"; $PSCmdlet.MyInvocation.BoundParameters.Keys | ForEach { Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "$($_)"; } } ##### Override The End Method End { Write-Debug "Override End Method"; Write-Verbose ""; ##### Stop The StopWatch $objCmdStopWatch.Stop(); Write-Verbose "Command Elapsed: ""$($objCmdStopWatch.ElapsedMilliseconds)"" Milliseconds." ; Write-Verbose "End!"; } } ##### Connect-SnsAzureAd ========================================================== Function Connect-SnsAzureAd () { <# .SYNOPSIS Establishes A Remote PowerShell Session To Office 365 AzureAD V2 Service. .DESCRIPTION Establishes A Remote PowerShell Session To Office 365 AzureAD V2 Service. The CmdLet Have Five Parameter Sets Related With The Authentication Method: -- FolderPath Here Must Be Specified The UserName And The Full Absolute UNC Folder Path Where The Encrypted Password File Resides. -- FilePath Here Must Be Provided The Full Absolute UNC Path To The Credential File. The CmdLet Will Try To Enumerate The UserName From The FileName. -- Credential Here Must Be Provided System.Management.Automation.PSCredential Object. -- CertificateThumbprint Uses Microsoft Authentication Libraries (MSAL): https://bit.ly/3x4bhEH. For That Purpose An Azure App Registration With Service Principal Have To Be Created And The Required Roles To Be Assigned To The Service Principal As Described Here: https://bit.ly/3FuQRYj -- Interactive This Is The Only Parameter Set Which Is Capable To Establish Remote PowerShell Session To AzureAD V2 Service With Multifactor Authentication. However It Cannot Be Used Whenever The Script Or The Function Is Executed In As Service Mode. In This Parameter Set The CmdLet Opens A Window Where The User Can Specify His Credentials And Multi Factor Authentication Code Received On A SMS Or Inside A Phone App Or Phone Call And Etc. Obviously When The CmdLet Is Executed As Service There Is No Real Person To Specify The Credentials And The MFA Code. Depending Of The Configuration There Might Not Be PowerShell Host Console Window Either. NOTE: The CmdLet Requires AzureAD V2 Module To Be Installed In Advance. Please Refer To https://bit.ly/30VWxaV NOTE: There Must Be A Direct Connection / Firewall Openings To Office 365. Proxy Usage Is Not Allowed. .PARAMETER UserName Specifies The UserName In UPN Format. Parameter Set: FolderPath Parameter Alias: N/A Parameter Validation: Yes Via RegEx Matching .PARAMETER FolderPath Specifies The Full Absolute UNC Folder Path Where The Credential File Resides. Parameter Set: FolderPath Parameter Alias: N/A Parameter Validation: Yes Via Directory Existence Validation .PARAMETER FilePath Specifies The Full Absolute UNC Path To The Credential File. Parameter Set: FilePath Parameter Alias: CredentialFile Parameter Validation: Yes Using File Existence And File Extension Validation .PARAMETER Credential Specifies [System.Management.Automation.PSCredential] Object. Parameter Set: Credential Parameter Alias: N/A Parameter Validation: N/A .PARAMETER CertificateThumbprint Specifies The Thumbprint Of A Certificate Used To Authenticate As Azure ServicePrincipal. Parameter Set: CertificateThumbprint Parameter Alias: N/A Parameter Validation: N/A .PARAMETER ApplicationId Specifies The ApplicationId Of Azure ServicePrincipal. Parameter Set: CertificateThumbprint Parameter Alias: N/A Parameter Validation: Yes Using RegEx Matching Validation .PARAMETER Tenant Specifies The TenantId. Parameter Set: CertificateThumbprint Parameter Alias: TenantId Parameter Validation: Yes Using RegEx Matching Validation .PARAMETER Interactive Specifies That The User Have To Be Asked Interactively For Credentials. Parameter Set: Interactive Parameter Alias: N/A Parameter Validation: N/A .PARAMETER Attempts Specifies The Number Of Attempts That Have To Be Made To Establish The Remote Session. Parameter Set: All Parameter Alias: N/A Parameter Validation: N/A .PARAMETER EventSource Specifies The Application Log Event Source To Be Used For The Error Event Logging. Parameter Set: All Parameter Alias: ScriptName Parameter Validation: N/A .PARAMETER PassThru Specifies That The CmdLet Have To Revert A Verification Collection. Parameter Set: All Parameter Alias: N/A Parameter Validation: N/A .INPUTS Pipeline Input None .OUTPUTS [System.Array] Which Contains A List With The Available AzureAD License Objects. .NOTES AUTHOR: Svetoslav Nedyalkov Savov THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER. .EXAMPLE [System.Array]$arrAzureLicenses = Connect-SnsAzureAd -UserName 'john.smith@contoso.com' -FolderPath 'C:\'; .EXAMPLE [System.Array]$arrAzureLicenses = Connect-SnsAzureAd -FilePath 'C:\john.smith@contoso.com.ini'; .EXAMPLE [System.Array]$arrAzureLicenses = Connect-SnsAzureAd -Credential $objCredential; .EXAMPLE [System.Array]$arrAzureLicenses = Connect-SnsAzureAd -CertificateThumbprint "THISISADEMOTHUMBPRINT" ` -Tenant "00000000-0000-0000-0000-000000000000" -ApplicationId "00000000-0000-0000-0000-000000000000"; .EXAMPLE [System.Array]$arrAzureLicenses = Connect-SnsAzureAd -Interactive; .LINK svesavov / SnsPsModule - https://github.com/svesavov/SnsPsModule .LINK Svetoslav Savov on LinkedIn - https://www.linkedin.com/in/svetoslavsavov #> [CmdletBinding(PositionalBinding = $false, DefaultParameterSetName = "Interactive")] Param ( [Parameter(Mandatory = $true, ParameterSetName = "FolderPath", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateScript({("$($_)" -match "$([SnsPsModule.SnsPatterns]::SnsSmtpAddressPattern)")})] [ValidateNotNullOrEmpty()][System.String]$UserName, [Parameter(Mandatory = $true, ParameterSetName = "FolderPath", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateScript({("$($_)".Contains("\"))})] [ValidateScript({([System.IO.Directory]::Exists("$($_)"))})] [ValidateNotNullOrEmpty()][System.String]$FolderPath, [Parameter(Mandatory = $true, ParameterSetName = "FilePath", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [Alias("CredentialFile")] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateScript({("$($_)".EndsWith(".ini"))})] [ValidateScript({("$($_)".Contains("\"))})] [ValidateScript({([System.IO.File]::Exists("$($_)"))})] [ValidateNotNullOrEmpty()][System.String]$FilePath, [Parameter(Mandatory = $true, ParameterSetName = "Credential", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateNotNullOrEmpty()][System.Management.Automation.PSCredential]$Credential, [Parameter(Mandatory = $true, ParameterSetName = "CertificateThumbprint", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateScript({("$($_)" -match "$([SnsPsModule.SnsPatterns]::CertThumbprintPattern)")})] [ValidateNotNullOrEmpty()][System.String]$CertificateThumbprint, [Parameter(Mandatory = $true, ParameterSetName = "CertificateThumbprint", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateScript({("$($_)" -match "$([SnsPsModule.SnsPatterns]::GUIDPattern)")})] [ValidateNotNullOrEmpty()][System.String]$ApplicationId, [Parameter(Mandatory = $true, ParameterSetName = "CertificateThumbprint", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [Alias("TenantId")] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateScript({("$($_)" -match "$([SnsPsModule.SnsPatterns]::GUIDPattern)")})] [ValidateNotNullOrEmpty()][System.String]$Tenant, [Parameter(Mandatory = $false, ParameterSetName = "Interactive", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [Switch]$Interactive = $false, [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateNotNullOrEmpty()][System.Int32]$Attempts = 3, [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [Alias("ScriptName")] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateScript({([SnsPsModule.SnsEventLog]::VerifySnsEventSource("$($_)"))})] [AllowNull()][AllowEmptyString()][System.String]$EventSource, [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [Switch]$PassThru ) ##### Override The Begin Method Begin { Write-Debug "Override Begin Method"; Write-Verbose ""; Write-Verbose "Connect-SnsAzureAd"; Write-Verbose "ParameterSetName: $($PSCmdlet.ParameterSetName)`r`n"; Write-Verbose "ErrorAction: $($ErrorActionPreference)"; ##### Initialize New Measure Watch [System.Diagnostics.Stopwatch]$objCmdStopWatch = [System.Diagnostics.Stopwatch]::StartNew(); If ("$((Get-FileHash -Path ""$($PSCommandPath)"" -Algorithm ""SHA256"" -Verbose:$false -Debug:$false).Hash)" -ne "$($global:SnsModuleCfg.ModuleHash)") { Write-Warning "There Is New Version Of SnsPsModule Module Released. Please Restart The PowerShell Session." -WarningAction "Continue"; }; ##### Initialize The Variables [System.Management.Automation.PSCredential]$objCredential = $null; [System.Collections.Specialized.OrderedDictionary]$hshSplat = [Ordered]@{}; [System.Int32]$intI = 0; [System.Array]$arrClLics = @(); #================================================================================== #region Enumerate The Credentials Object #================================================================================== ##### Load AzureADPreview PowerShell Module Write-Debug "Load AzureADPreview PowerShell Module"; If (Import-SnsModuleHelper -Module "AzureADPreview" -EventSource "$($EventSource)") { Return; } ###### Generate The Credential Object In FolderPath Parameter Set Write-Debug "Verify The Parameter Set Name"; [System.Management.Automation.PSCredential]$objCredential = $null; Switch ("$($PSCmdlet.ParameterSetName)") { "CertificateThumbprint" { Break; } "FilePath" { Write-Verbose "Import The Credential Object"; [System.Management.Automation.PSCredential]$objCredential = Import-SnsCredentialFile -FilePath "$($FilePath)" -EventSource "$($EventSource)" -Verbose:$false -Debug:$false; Break; } "FolderPath" { Write-Verbose "Import The Credential Object"; [System.Management.Automation.PSCredential]$objCredential = Import-SnsCredentialFile -UserName "$($UserName)" -FolderPath "$($FolderPath)" -EventSource "$($EventSource)" -Verbose:$false -Debug:$false; Break; } "Credential" { Write-Verbose "Assign The Provided Credential Object"; [System.Management.Automation.PSCredential]$objCredential = $Credential; Break; } } ##### Verify The Credential Object Write-Debug "Verify The Credential Object"; If (("$($PSCmdlet.ParameterSetName)" -ne "CertificateThumbprint") -and (-not [System.Environment]::UserInteractive) -and (-not "$($objCredential.UserName)")) { [System.Management.Automation.PSCredential]$objCredential = $null; [System.String]$strEventMessage = "Failed To Enumerate The AzureAd V2 Service Credential."; Log-SnsEventLogMessageHelper -Message "$($strEventMessage)" -EventLogEntryType "Error" -EventSource "$($EventSource)" -Verbose:$false -Debug:$false; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strEventMessage"; Return; } #================================================================================== #endregion Enumerate The Credentials Object #================================================================================== #================================================================================== #region Create AzureAD V2 Service Connection #================================================================================== ##### Verify Any Previous Sessions Existence Write-Debug "Verify Any Previous Sessions Existence"; If (-not -not (Get-Variable -Verbose:$false -Debug:$false | Where-Object -Verbose:$false -Debug:$false {"$($_.Name)" -like "ArrAzureAdLicenses"})) { Write-Verbose "Removing Previous Sessions"; Disconnect-AzureAD -Verbose:$false -Debug:$false; } ##### Generate The Connect-AzureAD Splatting HashTable Write-Debug "Generate The Connect-AzureAD Splatting HashTable"; [System.Collections.Specialized.OrderedDictionary]$hshSplat = [Ordered]@{}; $hshSplat.Add("Verbose", $false); $hshSplat.Add("Debug", $false); ##### Verify The ParameterSetName Write-Debug "Verify The ParameterSetName"; Switch ("$($PSCmdlet.ParameterSetName)") { "CertificateThumbprint" { $hshSplat.Add("TenantId", "$($Tenant)"); $hshSplat.Add("ApplicationId", "$($ApplicationId)"); $hshSplat.Add("CertificateThumbprint", "$($CertificateThumbprint)"); Break; } "Interactive" { #================================================================================== #region Interactively Authenticate Against AzureAD #================================================================================== ##### Verify Whether It Is The First Interactive Logon Write-Debug "Verify Whether It Is The First Interactive Logon"; If ( ` (-not (Get-Variable -Verbose:$false -Debug:$false | Where-Object -Verbose:$false -Debug:$false {"$($_.Name)" -like "AzureAdAccount"})) ` -or ` (-not (Get-Variable -Verbose:$false -Debug:$false | Where-Object -Verbose:$false -Debug:$false {"$($_.Name)" -like "AzureAdTenantId"})) ` ) { ##### Load AzureRM.Profile PowerShell Module Write-Debug "Load AzureRM.Profile PowerShell Module"; If (Import-SnsModuleHelper -Module "AzureRM.Profile" -EventSource "$($EventSource)") { Return; } ##### Loop The Interactive Login Process Write-Debug "Loop The Interactive Login Process"; [System.Int32]$intI = 0; [System.Object]$objRmAccount = $null; [System.String]$strAccountId = ""; [System.String]$strTenantId = ""; Do { ##### LogOn To RmAccount Write-Verbose "LogOn To RmAccount"; [System.Object]$objRmAccount = $null; [System.Object]$objRmAccount = Login-AzureRmAccount -Verbose:$false -Debug:$false; ##### Generate The Account ID Write-Debug "Generate The Account ID"; [System.String]$strAccountId = ""; [System.String]$strAccountId = "$($objRmAccount.Context.Account.Id)"; ##### Generate The Tenant ID Write-Debug "Generate The Tenant ID"; [System.String]$strTenantId = ""; [System.String]$strTenantId = "$(($objRmAccount.Context.Tenant | Where-Object {""$($strAccountId)"" -like ""*$($_.Directory)""} -Verbose:$false -Debug:$false)[0].Id)"; ##### Increment The Counter [System.Int32]$intI = $intI + 1; } While (((-not "$($strAccountId)") -or (-not "$($strTenantId)")) -and ($intI -lt $Attempts)) ##### Verify The Token Generation Write-Debug "Verify The Token Generation"; If ((-not -not "$($strAccountId)") -and (-not -not "$($strTenantId)")) { ##### Generate The Token Global Variables New-Variable -Scope "Global" -Option "Constant" -Name "AzureAdAccount" -Value "$($strAccountId)"; New-Variable -Scope "Global" -Option "Constant" -Name "AzureAdTenantId" -Value "$($strTenantId)"; } ##### Reset The Variables Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strTenantId"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strAccountId"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "objRmAccount"; } ##### Verify The Token Generation Write-Debug "Verify The Token Generation"; If ((-not -not "$($global:AzureAdAccount)") -and (-not -not "$($global:AzureAdTenantId)")) { ##### Add The Token Parameters To The Splatting HashTable $hshSplat.Add("TenantId", "$($global:AzureAdTenantId)"); $hshSplat.Add('AccountId', "$($global:AzureAdAccount)"); } Else { [System.String]$strEventMessage = "Failed To Interactively Authenticate Against AzureAD V2 Service."; Log-SnsEventLogMessageHelper -Message "$($strEventMessage)" -EventLogEntryType "Error" -EventSource "$($EventSource)" -Verbose:$false -Debug:$false; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strEventMessage"; Return; } #================================================================================== #endregion Interactively Authenticate Against AzureAD #================================================================================== Break; } default { $hshSplat.Add("Credential", $objCredential); } } ##### Loop The Session Creation Write-Debug "Loop The Session Creation"; [System.Int32]$intI = 0; [System.Array]$arrClLics = @(); Do { ##### Display The Action On The Console Write-Verbose "Connecting To AzureAD V2 Service."; Connect-AzureAD @hshSplat | Out-Null; Start-Sleep -Seconds $(2 * $intI) -Verbose:$false -Debug:$false; [System.Int32]$intI = $intI + 1; ##### Verify The PowerShell Session To The AzureAD [System.Array]$arrClLics = @(); [System.Array]$arrClLics = Get-AzureADSubscribedSku -Verbose:$false -Debug:$false | ` Select-Object -Property @("SkuPartNumber", "SkuId", "ServicePlans", "ConsumedUnits") -ExpandProperty "PrepaidUnits" -Verbose:$false -Debug:$false; ##### } While (($arrClLics.Count -lt 1) -and ($intI -lt $Attempts)) ##### Verify The AzureAD Session Creation Write-Debug "Verify The AzureAD Session Creation"; If ($arrClLics.Count -lt 1) { [System.String]$strEventMessage = "Failed To Establish A Connection To AzureAD V2 Service"; Log-SnsEventLogMessageHelper -Message "$($strEventMessage)" -EventLogEntryType "Error" -EventSource "$($EventSource)" -Verbose:$false -Debug:$false; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strEventMessage"; Return; } Else { ##### Verify Whether The Global AzureAd Licenses Variable Exists Write-Debug "Verify Whether The Global AzureAd Licenses Variable Exists"; If (-not (Get-Variable -Verbose:$false -Debug:$false | Where-Object -Verbose:$false -Debug:$false {"$($_.Name)" -like "ArrAzureAdLicenses"})) { ##### Create The Global AzureAd Licenses Variable Write-Debug "Create The Global AzureAd Licenses Variable"; New-Variable -Scope "Global" -Option "Constant" -Name "ArrAzureAdLicenses" -Value ($arrClLics); } } #================================================================================== #endregion Create AzureAD V2 Service Connection #================================================================================== ##### Reset The Variables Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "intI"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "hshSplat"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "objCredential"; } ##### Override The End Method End { Write-Debug "Override End Method"; Write-Verbose ""; ##### Stop The StopWatch $objCmdStopWatch.Stop(); Write-Verbose "Command Elapsed: ""$($objCmdStopWatch.ElapsedMilliseconds)"" Milliseconds." ; Write-Verbose "End!"; ##### Continue If Output Is Requested Write-Debug "Continue If Output Is Requested"; If (($PassThru.IsPresent) -and ($arrClLics.Count -gt 0)) { ##### Pass The Output Object To The Pipeline Write-Debug "Pass Output Object To The Pipeline"; Return $arrClLics; } $PSCmdlet.MyInvocation.BoundParameters.Keys | ForEach { Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "$($_)"; } } } ##### Connect-SnsExchangeEws ====================================================== Function Connect-SnsExchangeEws () { <# .SYNOPSIS This CmdLet Establish PowerShell Session To ExchangeOnline EWS Service Either On Premises Or Exchange Online. .DESCRIPTION This CmdLet Establish PowerShell Session To ExchangeOnline EWS Service Either On Premises Or Exchange Online. The CmdLet Uses Basic Authentication, With Other Words By The End Of The Year It Will Stop Working With Exchange Online. Please Consider Writing Of An Application With OAuth Authentication Against The Exchange Online EWS. The CmdLet Will Continue To Work With Exchange Server. In Case The Session Creation Fail The CmdLet Can Log An Event In The Windows Event Viewer Application Log And Kill The Script Process. This Functionality Is Enabled Automatically When EventSource Parameter Is Provided. Simple Throwing Terminating Error Will Keep The PowerShell Process. Which Will Prevent The Next Script Instances From Execution. And Any Possible Script Monitoring Will Be Cheated That The Script Is Still Running. The CmdLet Have Four Parameter Sets Related With The Way It Authenticates Against EWS: -- FolderPath - Must Be Specified The UserName And The Full Absolute UNC Folder Path Where The Encrypted Password File Resides. -- FilePath - Must Be Provided The Full Absolute UNC Path To The Credential File. The CmdLet Will Try To Enumerate The UserName From The FileName. -- Credential - Must Be Provided System.Management.Automation.PSCredential Object. -- Interactive It Cannot Be Used Whenever The Script Or The CmdLet Is Executed In As Service Mode. In This Parameter Set The CmdLet Opens A Window Where The User Can Specify His Credentials. Obviously When The CmdLet Is Executed As Service There Is No Real Person To Specify The Credentials. .PARAMETER Mailbox Specifies The PrimarySmtpAddress Of A Mailbox Used By The CmdLet To Enumerate The Connection Destination Via Exchange Autodiscover. If Omitted The CmdLet Will Try To Enumerate The Mailbox Of The Account Used For The Connection Authentication. Parameter Set: All Parameter Alias: N/A Parameter Validation: Yes Using RegEx Pattern Matching Validation .PARAMETER UserName Specifies The UserName Of The Account Used To Authenticate Against EWS. Parameter Set: FolderPath Parameter Alias: N/A Parameter Validation: Yes Using RegEx Pattern Matching Validation .PARAMETER FolderPath Specifies The Full Absolute UNC Folder Path Where The Credential File Resides. Parameter Set: FolderPath Parameter Alias: N/A Parameter Validation: Yes Using Folder Existence Validation .PARAMETER FilePath Specifies The Full Absolute UNC Path To The Credential File. Parameter Set: FilePath Parameter Alias: CredentialFile Parameter Validation: Yes Using File Existence And File Extension Validation .PARAMETER Credential Specifies [System.Management.Automation.PSCredential] Object. Parameter Set: Credential Parameter Alias: N/A Parameter Validation: N/A .PARAMETER Interactive Specifies That The User Have To Be Asked Interactively For Credentials. Parameter Set: Interactive Parameter Alias: N/A Parameter Validation: N/A .PARAMETER Attempts Specifies The Number Of Attempts That Have To Be Made To Establish The Remote Session. Parameter Set: All Parameter Alias: N/A Parameter Validation: N/A .PARAMETER EventSource Specifies The Application Log Event Source To Be Used For The Error Event Logging. Parameter Set: All Parameter Alias: ScriptName Parameter Validation: N/A .PARAMETER PassThru Specifies That The CmdLet Have To Revert A Verification Collection. Parameter Set: All Parameter Alias: N/A Parameter Validation: N/A .INPUTS Pipeline Input None .OUTPUTS [System.Object] Which Contains The Exchange Service Object. The Output Is Provided Only If Requested Because The CmdLet Normally Creates A Global Variable With That Object. The Global Variable Is: $global:ObjEwsService .NOTES AUTHOR: Svetoslav Nedyalkov Savov THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER. .EXAMPLE [System.Object]$objExchSrvc = Connect-SnsExchangeEws -UserName "john.smith@contoso.com" -FolderPath "C:\"; .EXAMPLE [System.Object]$objExchSrvc = Connect-SnsExchangeEws -FilePath "C:\john.smith@contoso.com.ini"; .EXAMPLE [System.Object]$objExchSrvc = Connect-SnsExchangeEws -Credential $objCredential; .EXAMPLE [System.Object]$objExchSrvc = Connect-SnsExchangeEws -Interactive; .LINK svesavov / SnsPsModule - https://github.com/svesavov/SnsPsModule .LINK Svetoslav Savov on LinkedIn - https://www.linkedin.com/in/svetoslavsavov #> [CmdletBinding(PositionalBinding = $false, DefaultParameterSetName = "Interactive")] Param ( [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateScript({("$($_)" -match "$([SnsPsModule.SnsPatterns]::SnsSmtpAddressPattern)")})] [ValidateNotNullOrEmpty()][System.String]$Mailbox = "", [Parameter(Mandatory = $true, ParameterSetName = "FolderPath", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateScript({(("$($_)".Contains("\")) -or ("$($_)" -match "$([SnsPsModule.SnsPatterns]::SnsSmtpAddressPattern)"))})] [ValidateNotNullOrEmpty()][System.String]$UserName, [Parameter(Mandatory = $true, ParameterSetName = "FolderPath", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateScript({("$($_)".Contains("\"))})] [ValidateScript({([System.IO.Directory]::Exists("$($_)"))})] [ValidateNotNullOrEmpty()][System.String]$FolderPath, [Parameter(Mandatory = $true, ParameterSetName = "FilePath", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [Alias("CredentialFile")] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateScript({("$($_)".EndsWith(".ini"))})] [ValidateScript({("$($_)".Contains("\"))})] [ValidateScript({([System.IO.File]::Exists("$($_)"))})] [ValidateNotNullOrEmpty()][System.String]$FilePath, [Parameter(Mandatory = $true, ParameterSetName = "Credential", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateNotNullOrEmpty()][System.Management.Automation.PSCredential]$Credential, [Parameter(Mandatory = $false, ParameterSetName = "Interactive", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [Switch]$Interactive = $false, [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateNotNullOrEmpty()][System.Int32]$Attempts = 3, [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [Alias("ScriptName")] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateScript({([SnsPsModule.SnsEventLog]::VerifySnsEventSource("$($_)"))})] [AllowNull()][AllowEmptyString()][System.String]$EventSource, [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [Switch]$PassThru ) ##### Override The Begin Method Begin { Write-Debug "Override Begin Method"; Write-Verbose ""; Write-Verbose "Connect-SnsExchangeEws"; Write-Verbose "ParameterSetName: $($PSCmdlet.ParameterSetName)`r`n"; ##### Initialize New Measure Watch [System.Diagnostics.Stopwatch]$objCmdStopWatch = [System.Diagnostics.Stopwatch]::StartNew(); If ("$((Get-FileHash -Path ""$($PSCommandPath)"" -Algorithm ""SHA256"" -Verbose:$false -Debug:$false).Hash)" -ne "$($global:SnsModuleCfg.ModuleHash)") { Write-Warning "There Is New Version Of SnsPsModule Module Released. Please Restart The PowerShell Session." -WarningAction "Continue"; }; Write-Warning "If You Are Using This CmdLet To Connect To Exchange Online EWS Please Consider Building Your Own App Using OAuth Authentication."; Write-Warning "This CmdLet Will Stop Working With Exchange Online Becuase It Uses Basic Authentication."; #================================================================================== #region Load Exchange Web Services Library #================================================================================== ##### Search About EWS Module Write-Debug "Search About EWS Module"; [System.IO.FileInfo[]]$arrEwsModules = @(); [System.IO.FileInfo[]]$arrEwsModules = Get-ChildItem -Path "C:\Program Files\Microsoft\Exchange\Web Services" -Filter "Microsoft.Exchange.WebServices.dll" ` -Recurse:$true -Verbose:$false -Debug:$false | Sort-Object -Property "LastWriteTimeUtc" -Verbose:$false -Debug:$false; ##### ##### Verify The Search Output Write-Debug "Verify The Search Output"; If ($arrEwsModules.Count -le 0) { [System.String]$strEventMessage = "Exchange Web Services Library Is Not Installed`r`nPlease Refer To https://bit.ly/2vEg18e"; Log-SnsEventLogMessageHelper -Message "$($strEventMessage)" -EventLogEntryType "Error" -EventSource "$($EventSource)" -Verbose:$false -Debug:$false; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strEventMessage"; Return; } ##### Load Exchange Web Services Library Write-Debug "Load Exchange Web Services Library"; If ($arrEwsModules.Count -gt 0) { Write-Host "Load Exchange Web Services Library" -ForegroundColor "Green"; [System.String]$strVerbosePreference = "$($VerbosePreference)"; $VerbosePreference = "SilentlyContinue"; Import-Module -Name "$($arrEwsModules[0].FullName)" -Global:$true -Force:$true -Verbose:$false -Debug:$false; $VerbosePreference = "$($strVerbosePreference)"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strVerbosePreference"; } ##### Reset The Variables Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "arrEwsModules"; #================================================================================== #endregion Load Exchange Web Services Library #================================================================================== ##### Initialize The Variables [System.Management.Automation.PSCredential]$objCredential = $null; [System.Int32]$intI = 0; [System.Net.NetworkCredential]$objNwCred = $null; [Microsoft.Exchange.WebServices.Data.WebCredentials]$objEwsCred = $null; [Microsoft.Exchange.WebServices.Data.ExchangeService]$objExchSrvc = $null; #================================================================================== #region Initialize The Credentials Object #================================================================================== ###### Verify The ParameterSetName Write-Debug "Verify The ParameterSetName"; [System.Management.Automation.PSCredential]$objCredential = $null; Switch ("$($PSCmdlet.ParameterSetName)") { "FilePath" { Write-Verbose "Import The Credential From File"; [System.Management.Automation.PSCredential]$objCredential = Import-SnsCredentialFile -FilePath "$($FilePath)" -EventSource "$($EventSource)" -Verbose:$false -Debug:$false; Break; } "FolderPath" { Write-Verbose "Import The Credential From File"; [System.Management.Automation.PSCredential]$objCredential = Import-SnsCredentialFile -UserName "$($UserName)" -FolderPath "$($FolderPath)" -EventSource "$($EventSource)" -Verbose:$false -Debug:$false; Break; } "Credential" { Write-Verbose "Assign The Provided Credential Object"; [System.Management.Automation.PSCredential]$objCredential = $Credential; Break; } } ##### Verify If It Is Interactive Session And There Are No Credentials Write-Debug "Verify If It Is Interactive Session And There Are No Credentials"; If ((-not "$($objCredential.UserName)") -and [System.Environment]::UserInteractive) { ##### Loop Interactive Credentials Dialog With The User Write-Debug "Loop Interactive Credentials Dialog With The User"; [System.Int32]$intI = 0; Do { ##### Ask The User About Credentials Write-Verbose "Ask The User About Credentials"; [System.Management.Automation.PSCredential]$objCredential = $null; [System.Management.Automation.PSCredential]$objCredential = Get-Credential -Verbose:$false -Debug:$false; ##### Check The Imported Credentials Write-Debug "Check The Imported Credentials"; If (-not "$($objCredential.UserName)") { ##### Generate The Error Message Write-Error "Provided Invalid Credentials" -ErrorAction "Continue"; [System.Management.Automation.PSCredential]$objCredential = $null; } ##### Increment The Counter [System.Int32]$intI = $intI + 1; } While ((-not "$($objCredential.UserName)") -and ($intI -lt $Attempts)) } ##### Verify The Credentials Object Write-Debug "Verify The Credentials Object"; If (-not "$($objCredential.UserName)") { [System.Management.Automation.PSCredential]$objCredential = $null; [System.String]$strEventMessage = "Failed To Enumerate The Credential Object For Exchange EWS."; Log-SnsEventLogMessageHelper -Message "$($strEventMessage)" -EventLogEntryType "Error" -EventSource "$($EventSource)" -Verbose:$false -Debug:$false; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strEventMessage"; Return; } #================================================================================== #endregion Initialize The Credentials Object #================================================================================== #================================================================================== #region Enumerate The Destination Mailbox #================================================================================== ##### Verify Whether The User Omitted Mailbox Parameter Write-Debug "Verify Whether The User Omitted Mailbox Parameter"; If (-not "$($Mailbox)") { ##### Verify Whether The CmdLet Is Run In ActiveDirectory Domain Joined Computer Write-Debug "Verify Whether The CmdLet Is Run In ActiveDirectory Domain Joined Computer"; If ([System.DirectoryServices.DirectoryEntry]::Exists("")) { ##### Enumerate The LDAP Query Write-Debug "Enumerate The LDAP Query"; [System.String]$strTmp = ""; [System.String]$strTmp = "$($objCredential.UserName)"; [System.String]$strTmp = "$(""$($strTmp)"" -replace ""^.+\\"", """")"; [System.String]$strTmp = "(&(objectCategory=Person)(objectClass=user)(|(sAMAccountName=$($strTmp))(userPrincipalName=$($strTmp))))"; ##### Search The Active Directory About The Specified In The Credential Account Write-Verbose "Search The Active Directory About The Specified In The Credential Account"; [System.DirectoryServices.SearchResultCollection]$arrResults = $null; [System.DirectoryServices.SearchResultCollection]$arrResults = Search-SnsAdObject -LdapQuery "$($strTmp)" ` -ReturnProperties @("distinguishedName", "proxyAddresses") -ResultSize 2 -GcSearch:$true -Verbose:$false -Debug:$false; ##### ##### Verify The Search Output Write-Debug "Verify The Search Output"; If (($arrResults.Count -eq 1) -and ($arrResults[0].Properties.proxyaddresses.Count -gt 0)) { ##### Enumerate The PrimarySmstpAddress Write-Debug "Enumerate The PrimarySmstpAddress"; [System.String]$strTmp = ""; [System.String]$strTmp = "$((@($arrResults[0].Properties.proxyaddresses | Where-Object {""$($_)"" -clike ""SMTP:*""} -Verbose:$false -Debug:$false))[0].Replace(""SMTP:"", """").Trim())"; ##### Verify Whether The Enumerated PrimarySmstpAddress Match The SMTP RegEx Pattern Write-Debug "Verify Whether The Enumerated PrimarySmstpAddress Match The SMTP RegEx Pattern"; If ((-not -not "$($strTmp)") -and ("$($strTmp)" -match "$([SnsPsModule.SnsPatterns]::SnsSmtpAddressPattern)")) { Write-Verbose "Enumerated Destination Mailbox ""$($strTmp)""."; [System.String]$Mailbox = "$($strTmp)"; } } ##### Reset The Variables Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "arrResults"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strTmp"; ##### Verify Whether The Destination Mailbox Is Still Not Enumerated Write-Debug "Verify Whether The Destination Mailbox Is Still Not Enumerated"; If ((-not "$($Mailbox)") -and ("$($objCredential.UserName)" -match "$([SnsPsModule.SnsPatterns]::SnsSmtpAddressPattern)")) { ##### Assuming That The UPN Equals The Mailbox PrimarySmstpAddress Write-Verbose "Using The UPN From Credentials As Mailbox."; [System.String]$Mailbox = "$($objCredential.UserName)"; } } Else { ##### Verify Whether The UserName In The Credentials Object Is UPN Write-Debug "Verify Whether The UserName In The Credentials Object Is UPN"; If ("$($objCredential.UserName)" -match "$([SnsPsModule.SnsPatterns]::SnsSmtpAddressPattern)") { ##### Assuming That The UPN Equals The Mailbox PrimarySmstpAddress Write-Verbose "Using The UPN From Credentials As Mailbox."; [System.String]$Mailbox = "$($objCredential.UserName)"; } } } ##### Verify Whether The Destination Mailbox Is Still Not Enumerated Write-Debug "Verify Whether The Destination Mailbox Is Still Not Enumerated"; If (-not "$($Mailbox)") { [System.Management.Automation.PSCredential]$objCredential = $null; [System.String]$strEventMessage = "Failed To Enumerate The Destination Mailbox."; Log-SnsEventLogMessageHelper -Message "$($strEventMessage)" -EventLogEntryType "Error" -EventSource "$($EventSource)" -Verbose:$false -Debug:$false; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strEventMessage"; Return; } #================================================================================== #endregion Enumerate The Destination Mailbox #================================================================================== #================================================================================== #region Create Exchange EWS Session #================================================================================== ##### Set The Domain In GetNetworkCredential Write-Debug "Set The Domain In GetNetworkCredential"; [System.Net.NetworkCredential]$objNwCred = $null; [System.Net.NetworkCredential]$objNwCred = $objCredential.GetNetworkCredential(); $objNwCred.Domain = "$((""$($objCredential.UserName)"" -replace ""^.+\@"", """") -replace ""\\.+$"","""")"; ##### Loop The Session Creation Write-Debug "Loop The Session Creation"; [System.Int32]$intI = 0; [Microsoft.Exchange.WebServices.Data.WebCredentials]$objEwsCred = $null; [Microsoft.Exchange.WebServices.Data.ExchangeService]$objExchSrvc = $null; Do { ##### Create WebCredentials Object Write-Debug "Create WebCredentials Object"; [Microsoft.Exchange.WebServices.Data.WebCredentials]$objEwsCred = $null; [Microsoft.Exchange.WebServices.Data.WebCredentials]$objEwsCred = New-Object -TypeName "Microsoft.Exchange.WebServices.Data.WebCredentials" ` -ArgumentList ( $objNwCred.Username, $objNwCred.Password, $objNwCred.Domain ) -Verbose:$false -Debug:$false; ##### ##### Create The ExchangeService Object Write-Debug "Create The ExchangeService Object"; [Microsoft.Exchange.WebServices.Data.ExchangeService]$objExchSrvc = $null; [Microsoft.Exchange.WebServices.Data.ExchangeService]$objExchSrvc = New-Object -TypeName "Microsoft.Exchange.WebServices.Data.ExchangeService" -Verbose:$false -Debug:$false; $objExchSrvc.UseDefaultCredentials = $true; $objExchSrvc.Credentials = $objEwsCred; ##### Establish The EWS Session Write-Verbose "Establish The EWS Session"; $objExchSrvc.AutodiscoverUrl("$($Mailbox)", {$true}); ##### Process The Loop Variable And TimeOut Start-Sleep -Seconds $(2 * $intI) -Verbose:$false -Debug:$false; [System.Int32]$intI = $intI + 1; } While ((-not "$($objExchSrvc.Url.AbsoluteUri)") -and ($intI -lt $Attempts)) ##### Verify The EWS Session Creation Write-Verbose "Verify The EWS Session Creation"; If (-not "$($objExchSrvc.Url.AbsoluteUri)") { [System.Management.Automation.PSCredential]$objCredential = $null; [System.String]$strEventMessage = "Failed To Establish Exchange EWS Session"; Log-SnsEventLogMessageHelper -Message "$($strEventMessage)" -EventLogEntryType "Error" -EventSource "$($EventSource)" -Verbose:$false -Debug:$false; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strEventMessage"; Return; } #================================================================================== #endregion Create Exchange EWS Session #================================================================================== ##### Verify The ExchangeService Session Creation Write-Debug "Verify The ExchangeService Session Creation"; If ((-not -not "$($objExchSrvc.Url.AbsoluteUri)") -and (-not (Get-Variable -Verbose:$false -Debug:$false | Where-Object {"$($_.Name)" -like 'ObjEwsService'} -Verbose:$false -Debug:$false))) { ##### Create The Global EWS Service Variable Write-Verbose "Create The Global EWS Service Variable ""ObjEwsService"""; New-Variable -Scope "Global" -Option "Constant" -Name 'ObjEwsService' -Value ($objExchSrvc); } ##### Reset The Variables Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "objEwsCred"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "intI"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "objCredential"; } ##### Override The End Method End { Write-Debug "Override End Method"; Write-Verbose ""; ##### Stop The StopWatch $objCmdStopWatch.Stop(); Write-Verbose "Command Elapsed: ""$($objCmdStopWatch.ElapsedMilliseconds)"" Milliseconds." ; Write-Verbose "End!"; ##### Continue If Output Is Requested Write-Debug "Continue If Output Is Requested"; If (($PassThru.IsPresent) -and (-not -not "$($objExchSrvc.Url.AbsoluteUri)")) { ##### Pass The Output Object To The Pipeline Write-Debug "Pass Output Object To The Pipeline"; Return $objExchSrvc; } $PSCmdlet.MyInvocation.BoundParameters.Keys | ForEach { Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "$($_)"; } } } ##### Connect-SnsExchangeOnline =================================================== Function Connect-SnsExchangeOnline () { <# .SYNOPSIS This CmdLet Establish Remote PowerShell Session To ExchangeOnline In Office 365. .DESCRIPTION This CmdLet Establish Remote PowerShell Session To ExchangeOnline In Office 365. In Case The Session Creation Fail The CmdLet Can Log An Event In The Windows Event Viewer Application Log And Kill The Script Process. This Functionality Is Enabled Automatically When Value Is Provided To EventSource Parameter. The CmdLet Have Six Parameter Sets Depending On The Way The CmdLet Authenticates Against ExchangeOnline: -- FolderPath Here Must Be Specified The UserName And The Full Absolute UNC Folder Path Where The Encrypted Password File Resides. -- FilePath Here Must Be Provided The Full Absolute UNC Path To The Credential File. The CmdLet Will Try To Generate The UserName From The FileName. -- Credential Here Must Be Provided System.Management.Automation.PSCredential Object. -- CertificateThumbprint Here Is Used The PartnerCenter PowerShell Module To Be Generated OAuthV2 AccessToken With Credential Object Used To Connect To Exchange Online Directly On The Exchange Online PowerShell APIs. In Case The PartnerCenter PowerShell Module Is Not Installed The CmdLet Will Fail With Error Asking To Install It. The CertificateThumbprint Parameter Set Uses Microsoft Authentication Libraries (MSAL): https://bit.ly/3x4bhEH. For That Purpose An Azure App Registration With Service Principal Have To Be Created And The Required Roles To Be Assigned To The Service Principal As Described Here: https://bit.ly/3FuQRYj -- ExchangeOnlineManagement Here Is Used The ExchangeOnlineManagement PowerShell Module To Establish A Remote Session To Exchange Online. In Case The PowerShell Module Is Not Installed On The Machine The CmdLet Will Fail. For More Details Please Visit https://bit.ly/3CzIs45 -- Interactive It Cannot Be Used Whenever The Script Or The CmdLet Is Executed In As Service Mode. In This Parameter Set The CmdLet Opens A Window Where The User Can Specify His Credentials. Obviously When The CmdLet Is Executed As Service There Is No Real Person To Specify The Credentials. .PARAMETER UserName Specifies The UserName. Parameter Set: FolderPath Parameter Alias: N/A Parameter Validation: Yes Using RegEx Pattern Matching .PARAMETER FolderPath Specifies The Full Absolute UNC Folder Path Where The Credential File Resides. Parameter Set: FolderPath Parameter Alias: N/A Parameter Validation: Yes Using Directory Existence Validation. .PARAMETER FilePath Specifies The Full Absolute UNC Path To The Credential File. Parameter Set: FilePath Parameter Alias: CredentialFile Parameter Validation: Yes Using The File Existence And File Extension Validation .PARAMETER Credential Specifies [System.Management.Automation.PSCredential] Object. Parameter Set: Credential Parameter Alias: N/A Parameter Validation: Yes Using Value Existence Validation .PARAMETER CertificateThumbprint Specifies The Thumbprint Of A Certificate Used To Authenticate As Azure ServicePrincipal. Parameter Set: CertificateThumbprint, ExchangeOnlineManagement Parameter Alias: N/A Parameter Validation: Yes Using Value Existence Validation .PARAMETER ApplicationId Specifies The ApplicationId Of Azure ServicePrincipal. Parameter Set: CertificateThumbprint, ExchangeOnlineManagement Parameter Alias: N/A Parameter Validation: Yes Using RegEx Pattern Matching Validation .PARAMETER UserPrincipalName Specifies The UserPrincipalName Of Azure ServicePrincipal. Parameter Set: CertificateThumbprint Parameter Alias: N/A Parameter Validation: Yes Using RegEx Pattern Matching Validation .PARAMETER Tenant Specifies The TenantId. Parameter Set: CertificateThumbprint Parameter Alias: N/A Parameter Validation: Yes Using RegEx Pattern Matching Validation .PARAMETER Organization Specifies The Organization Name. Parameter Set: ExchangeOnlineManagement Parameter Alias: N/A Parameter Validation: Yes Using Syntax Validation. .PARAMETER Interactive Specifies That The User Have To Be Asked Interactively For Credentials. Parameter Set: Interactive Parameter Alias: N/A Parameter Validation: N/A .PARAMETER Prefix Specifies The Prefix For The CmdLets In This Session. Parameter Set: All Parameter Alias: N/A Parameter Validation: Yes Using Value Existence Validation .PARAMETER ProxyAccessType Specifies The ProxyAccessType. The Best Practice Require Direct Internet Access To Office 365. Accepted Values: "IEConfig", "WinHttpConfig", "AutoDetect", "NoProxyServer", "None". Parameter Set: All Parameter Alias: N/A Parameter Validation: Yes Using Enumeration Validation .PARAMETER Attempts Specifies The Number Of Attempts That Have To Be Made To Establish The Remote Session. Parameter Set: All Parameter Alias: N/A Parameter Validation: N/A .PARAMETER EventSource Specifies The Application Log Event Source To Be Used For The Error Event Logging. Parameter Set: All Parameter Alias: ScriptName Parameter Validation: N/A .PARAMETER PassThru Specifies That The CmdLet Have To Revert A Verification Collection. Parameter Set: All Parameter Alias: N/A Parameter Validation: N/A .INPUTS Pipeline Input None .OUTPUTS [System.Array] Which Contains A List With The ExchangeOnline Accepted Domain Objects. .NOTES AUTHOR: Svetoslav Nedyalkov Savov THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER. .EXAMPLE [System.Array]$arrAcceptedDomains = Connect-SnsExchangeOnline -UserName "john.smith@contoso.com" ` -FolderPath "C:\" -PassThru; .EXAMPLE Connect-SnsExchangeOnline -FilePath "C:\john.smith@contoso.com.ini"; .EXAMPLE Connect-SnsExchangeOnline -Credential $objCredential; .EXAMPLE Connect-SnsExchangeOnline -CertificateThumbprint "THISISADEMOTHUMBPRINT" ` -ApplicationId "00000000-0000-0000-0000-000000000000" -UserPrincipalName "ApplicationUPN@contoso.onmicrosoft.com" ` -Tenant "00000000-0000-0000-0000-000000000000"; .EXAMPLE Connect-SnsExchangeOnline -Organization "contoso.onmicrosoft.com" ` -CertificateThumbprint "012THISISADEMOTHUMBPRINT" -ApplicationId "00000000-0000-0000-0000-000000000000"; .EXAMPLE Connect-SnsExchangeOnline -Interactive; .LINK svesavov / SnsPsModule - https://github.com/svesavov/SnsPsModule .LINK Svetoslav Savov on LinkedIn - https://www.linkedin.com/in/svetoslavsavov #> [Alias("New-ExoSession")] [CmdletBinding(PositionalBinding = $false, DefaultParameterSetName = "Interactive")] Param ( [Parameter(Mandatory = $true, ParameterSetName = "FolderPath", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateScript({("$($_)" -match "$([SnsPsModule.SnsPatterns]::SnsSmtpAddressPattern)")})] [ValidateNotNullOrEmpty()][System.String]$UserName, [Parameter(Mandatory = $true, ParameterSetName = "FolderPath", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateScript({("$($_)".Contains("\"))})] [ValidateScript({([System.IO.Directory]::Exists("$($_)"))})] [ValidateNotNullOrEmpty()][System.String]$FolderPath, [Parameter(Mandatory = $true, ParameterSetName = "FilePath", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [Alias("CredentialFile")] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateScript({("$($_)".EndsWith(".ini"))})] [ValidateScript({("$($_)".Contains("\"))})] [ValidateScript({([System.IO.File]::Exists("$($_)"))})] [ValidateNotNullOrEmpty()][System.String]$FilePath, [Parameter(Mandatory = $true, ParameterSetName = "Credential", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateNotNullOrEmpty()][System.Management.Automation.PSCredential]$Credential, [Parameter(Mandatory = $true, ParameterSetName = "CertificateThumbprint", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [Parameter(Mandatory = $true, ParameterSetName = "ExchangeOnlineManagement", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateNotNullOrEmpty()][System.String]$CertificateThumbprint, [Parameter(Mandatory = $true, ParameterSetName = "CertificateThumbprint", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [Parameter(Mandatory = $true, ParameterSetName = "ExchangeOnlineManagement", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateScript({("$($_)" -match "$([SnsPsModule.SnsPatterns]::GUIDPattern)")})] [ValidateNotNullOrEmpty()][System.String]$ApplicationId, [Parameter(Mandatory = $true, ParameterSetName = "CertificateThumbprint", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateScript({("$($_)" -match "$([SnsPsModule.SnsPatterns]::SnsSmtpAddressPattern)")})] [ValidateNotNullOrEmpty()][System.String]$UserPrincipalName, [Parameter(Mandatory = $true, ParameterSetName = "CertificateThumbprint", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateScript({("$($_)" -match "$([SnsPsModule.SnsPatterns]::GUIDPattern)")})] [ValidateNotNullOrEmpty()][System.String]$Tenant, [Parameter(Mandatory = $true, ParameterSetName = "ExchangeOnlineManagement", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateScript({("$($_)".EndsWith(".onmicrosoft.com"))})] [ValidateNotNullOrEmpty()][System.String]$Organization, [Parameter(Mandatory = $false, ParameterSetName = "Interactive", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [Switch]$Interactive = $false, [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateNotNullOrEmpty()][System.String]$Prefix, [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateSet("IEConfig", "WinHttpConfig", "AutoDetect", "NoProxyServer", "None")] [ValidateNotNullOrEmpty()][System.String]$ProxyAccessType = "NoProxyServer", [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateNotNullOrEmpty()][System.Int32]$Attempts = 3, [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [Alias("ScriptName")] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateScript({([SnsPsModule.SnsEventLog]::VerifySnsEventSource("$($_)"))})] [AllowNull()][AllowEmptyString()][System.String]$EventSource, [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [Switch]$PassThru ) ##### Override The Begin Method Begin { Write-Debug "Override Begin Method"; Write-Verbose ""; Write-Verbose "Connect-SnsExchangeOnline"; Write-Verbose "ParameterSetName: $($PSCmdlet.ParameterSetName)"; ##### Initialize New Measure Watch [System.Diagnostics.Stopwatch]$objCmdStopWatch = [System.Diagnostics.Stopwatch]::StartNew(); If ("$((Get-FileHash -Path ""$($PSCommandPath)"" -Algorithm ""SHA256"" -Verbose:$false -Debug:$false).Hash)" -ne "$($global:SnsModuleCfg.ModuleHash)") { Write-Warning "There Is New Version Of SnsPsModule Module Released. Please Restart The PowerShell Session." -WarningAction "Continue"; }; ##### Generate The Verbose Boolean Write-Debug "Generate The Verbose Boolean"; [System.Boolean]$bolVerbose = $false If ($PSCmdlet.MyInvocation.BoundParameters.Keys -icontains "Verbose") { [System.Boolean]$bolVerbose = $PSCmdlet.MyInvocation.BoundParameters.Verbose.IsPresent; } ##### Initialize The Variables [System.String]$strInitialVerboseSettings = "$($VerbosePreference)"; [System.Management.Automation.ScriptBlock]$scrBlock = $null; [System.Management.Automation.PSCredential]$objCredential = $null; [System.Management.Automation.PSModuleInfo]$objSesModule = $null; [System.Array]$arrAcceptedDomains = @(); ##### Generate The Verification Script Block [System.Management.Automation.ScriptBlock]$scrBlock = [System.Management.Automation.ScriptBlock]::Create("Get-$($Prefix)AcceptedDomain"); #================================================================================== #region Initialize The Credentials Object #================================================================================== ###### Generate The Credential Object In FolderPath Parameter Set Write-Debug "Verify The Parameter Set Name"; [System.Management.Automation.PSCredential]$objCredential = $null; Switch ("$($PSCmdlet.ParameterSetName)") { "ExchangeOnlineManagement" { Write-Debug "Load ExchangeOnlineManagement PowerShell Module"; If (Import-SnsModuleHelper -Module "ExchangeOnlineManagement" -EventSource "$($EventSource)") { Return; } Break; } "Interactive" { Write-Debug "Load ExchangeOnlineManagement PowerShell Module"; If (Import-SnsModuleHelper -Module "ExchangeOnlineManagement" -EventSource "$($EventSource)") { Return; } Break; } "FilePath" { Write-Verbose "Import The Credential From A File"; [System.Management.Automation.PSCredential]$objCredential = Import-SnsCredentialFile -FilePath "$($FilePath)" -EventSource "$($EventSource)" -Verbose:$false -Debug:$false; Break; } "FolderPath" { Write-Verbose "Import The Credential From A File"; [System.Management.Automation.PSCredential]$objCredential = Import-SnsCredentialFile -UserName "$($UserName)" -FolderPath "$($FolderPath)" -EventSource "$($EventSource)" -Verbose:$false -Debug:$false; Break; } "Credential" { Write-Verbose "Assign The Provided Credential Object"; [System.Management.Automation.PSCredential]$objCredential = $Credential; Break; } "CertificateThumbprint" { ##### Load PartnerCenter PowerShell Module Write-Debug "Load PartnerCenter PowerShell Module"; If (Import-SnsModuleHelper -Module "PartnerCenter" -EventSource "$($EventSource)") { Return; } ##### Initialize AccessToken With The Specified Service Principal Certificate Write-Verbose "Initialize AccessToken With The Specified Service Principal Certificate"; [Microsoft.Store.PartnerCenter.PowerShell.Models.Authentication.AuthResult]$objToken = $null; [Microsoft.Store.PartnerCenter.PowerShell.Models.Authentication.AuthResult]$objToken = New-PartnerAccessToken -ApplicationId "$($ApplicationId)" ` -CertificateThumbprint "$($CertificateThumbprint)" -Scopes "https://outlook.office365.com/.default" -ServicePrincipal:$true -Tenant "$($Tenant)" -Verbose:$false -Debug:$false; ##### ##### Initialize PSCredential Object With The Enumerated AccessToken Write-Verbose "Initialize PSCredential Object With The Enumerated AccessToken"; [System.Management.Automation.PSCredential]$objCredential = New-Object -TypeName "System.Management.Automation.PSCredential" -ArgumentList @( "$($UserPrincipalName)", $(ConvertTo-SecureString "Bearer $($objToken.AccessToken)" -AsPlainText:$true -Force:$true -Verbose:$false -Debug:$false) ); ##### Reset The Variables Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "objToken"; Break; } } ##### Verify The Credential Object Write-Debug "Verify The Credential Object"; If (("$($PSCmdlet.ParameterSetName)" -ne "ExchangeOnlineManagement") -and ("$($PSCmdlet.ParameterSetName)" -ne "Interactive") -and (-not "$($objCredential.UserName)")) { [System.Management.Automation.PSCredential]$objCredential = $null; [System.String]$strEventMessage = "Failed To Enumerate The Credential For ExchangeOnline"; Log-SnsEventLogMessageHelper -Message "$($strEventMessage)" -EventLogEntryType "Error" -EventSource "$($EventSource)" -Verbose:$false -Debug:$false; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strEventMessage"; Return; } #================================================================================== #endregion Initialize The Credentials Object #================================================================================== #================================================================================== #region Establish The ExchangeOnline Remote PSSession #================================================================================== ##### Remove Any Previous Sessions Write-Debug "Remove Any Previous Sessions"; Remove-SnsPreviousSessionsHelper -Filter "ExchangeOnlineInternalSession" -ScrBlock $scrBlock -Verbose:$bolVerbose -Debug:$false; ##### Verify The ParameterSetName Write-Debug "Verify The ParameterSetName"; [System.Management.Automation.PSModuleInfo]$objSesModule = $null; $VerbosePreference = "SilentlyContinue"; Switch ("$($PSCmdlet.ParameterSetName)") { "ExchangeOnlineManagement" { [System.Management.Automation.PSModuleInfo]$objSesModule = New-SnsExoSessionMsModuleHelper -Organization "$($Organization)" -ProxyAccessType "$($ProxyAccessType)" ` -CertificateThumbprint "$($CertificateThumbprint)" -ApplicationId "$($ApplicationId)" -Prefix "$($Prefix)" -Attempts $Attempts -Verbose:$bolVerbose -Debug:$false; Break; } "Interactive" { [System.Management.Automation.PSModuleInfo]$objSesModule = New-SnsExoSessionInteractiveHelper -Prefix "$($Prefix)" ` -Attempts $Attempts -ProxyAccessType "$($ProxyAccessType)" -Verbose:$bolVerbose -Debug:$false; Break; } default { [System.Management.Automation.PSModuleInfo]$objSesModule = New-SnsRemoteSessionNoModuleHelper -Credential $objCredential ` -Url "$(If (-not -not ""$($CertificateThumbprint)"") { ""https://ps.outlook.com/powershell-liveid?BasicAuthToOAuthConversion=true""; } Else { ""https://ps.outlook.com/powershell""; })" ` -Prefix "$($Prefix)" -ProxyAccessType "$($ProxyAccessType)" -Attempts $Attempts -EventSource "$($EventSource)" -Verbose:$bolVerbose -Debug:$false; ##### } } $VerbosePreference = "$($strInitialVerboseSettings)"; #================================================================================== #endregion Establish The ExchangeOnline Remote PSSession #================================================================================== #================================================================================== #region Retrieve The Accepted Domains Via The Remote Session #================================================================================== ##### Verify The PSSession Import Write-Debug "Verify The PSSession Import"; If (-not -not "$($objSesModule.Name)") { ##### Verify Whether Get-AcceptedDomain Command Is Among The Exported Commands Write-Debug "Verify Whether Get-AcceptedDomain Command Is Among The Exported Commands"; If ($objSesModule.ExportedCommands.Keys -icontains "Get-$($Prefix)AcceptedDomain") { ##### Generate The ExchangeOnline Accepted Domain Array Write-Debug "Generate The ExchangeOnline Accepted Domain Array"; [System.Array]$arrAcceptedDomains = @(); [System.Array]$arrAcceptedDomains = Invoke-Command -ScriptBlock $scrBlock -Verbose:$false -Debug:$false; ##### Verify The ExchangeOnline Accepted Domain Array Write-Debug "Verify The ExchangeOnline Accepted Domain Array"; If (($arrAcceptedDomains.Count) -eq 0) { [System.String]$strEventMessage = "Failed To Retrieve The ExchangeOnline AcceptedDomain"; Log-SnsEventLogMessageHelper -Message "$($strEventMessage)" -EventLogEntryType "Error" -EventSource "$($EventSource)" -Verbose:$false -Debug:$false; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strEventMessage"; Return; } } Else { Return; } } #================================================================================== #endregion Retrieve The Accepted Domains Via The Remote Session #================================================================================== ##### Reset The Variables Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "objSesModule"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "objCredential"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "scrBlock"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strInitialVerboseSettings"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "bolVerbose"; } ##### Override The End Method End { Write-Debug "Override End Method"; Write-Verbose ""; ##### Stop The StopWatch $objCmdStopWatch.Stop(); Write-Verbose "Command Elapsed: ""$($objCmdStopWatch.ElapsedMilliseconds)"" Milliseconds." ; Write-Verbose "End!"; ##### Continue If Output Is Requested Write-Debug "Continue If Output Is Requested"; If (($PassThru.IsPresent) -and ($arrAcceptedDomains.Count -gt 0)) { ##### Pass The Output Object To The Pipeline Write-Debug "Pass Output Object To The Pipeline"; Return $arrAcceptedDomains; } $PSCmdlet.MyInvocation.BoundParameters.Keys | ForEach { Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "$($_)"; } } } ##### Connect-SnsExchangeOnPremises =============================================== Function Connect-SnsExchangeOnPremises () { <# .SYNOPSIS Establishes A Remote PowerShell Session To Exchange On Premises CAS Server. .DESCRIPTION Establishes A Remote PowerShell Session To Exchange On Premises CAS Server. In Case The Session Creation Fail The CmdLet Can Log An Event In The Windows Event Viewer Application Log And Kill The Script Process. This Functionality Is Enabled Automatically When Value Is Provided To EventSource Parameter. The CmdLet Have Four Parameter Sets Depending On The Way That The CmdLet Authenticates Against The CAS Server: -- FolderPath Here Must Be Specified The UserName And The Full Absolute UNC Folder Path Where The Encrypted Password File Resides. -- FilePath Here Must Be Provided The Full Absolute UNC Path To The Credential File. The CmdLet Will Try To Enumerate The UserName From The FileName. -- Credential Here Must Be Provided System.Management.Automation.PSCredential Object. -- Interactive It Cannot Be Used Whenever The Script Or The Function Is Executed In As Service Mode. In This Parameter Set The CmdLet Opens A Window Where The User Can Specify His Credentials. Obviously When The CmdLet Is Executed As Service There Is No Real Person To Specify The Credentials. .PARAMETER HostName Specifies The Fully Qualified Domain Name Of An Exchange CAS Server Or a CAS Array. In Case The CAS Array Is Created With Hardware Load Balancer And The PowerShell Virtual Directory Is Not Published There The Connection Will Fail In This Scenario The Only Way To Connect Is Using CAS Server FQDN. If Omitted The CmdLet Will Try To Retrieve The CAS Servers From AD And Chose The Nearest One If ICMP Protocol Is Enabled In The Environment. If The ICMP Is Not Enabled The CmdLet Will Take The Last Server From The List Where The Enumerated CAS Servers Are Sorted Alphabetically. In Order The Automatic CAS Servers Enumeration To Work As Expected The Exchange Must Be Installed In The Root AD Domain. In Single Domain Environments It Always Work. Parameter Set: All Parameter Alias: N/A Parameter Validation: Yes Using DNS System Validation .PARAMETER UserName Specifies The UserName. Parameter Set: FolderPath Parameter Alias: N/A Parameter Validation: Yes Using RegEx Pattern Matching Validation .PARAMETER FolderPath Specifies The Full Absolute UNC Folder Path Where The Credential File Resides. Parameter Set: FolderPath Parameter Alias: N/A Parameter Validation: Yes Using Folder Existence Validation .PARAMETER FilePath Specifies The Full Absolute UNC Path To The Credential File. Parameter Set: FilePath Parameter Alias: CredentialFile Parameter Validation: Yes Using File Existence Validation .PARAMETER Credential Specifies [System.Management.Automation.PSCredential] Object. Parameter Set: Credential Parameter Alias: N/A Parameter Validation: Yes Using Impersonated LDAP Query Validation .PARAMETER Interactive Specifies That The User Have To Be Asked Interactively For Credentials. Parameter Set: Interactive Parameter Alias: N/A Parameter Validation: N/A .PARAMETER Protocol Specifies Whether HTTP Or HTTPS Protocol Shall Be Used. Parameter Set: All Parameter Alias: N/A Parameter Validation: Yes Using Enumeration Validation .PARAMETER Prefix Specifies The Prefix For The CmdLets In This Session. Parameter Set: All Parameter Alias: N/A Parameter Validation: Yes Using Value Existence Validation .PARAMETER Attempts Specifies The Number Of Attempts That Have To Be Made To Establish The Remote Session. Parameter Set: All Parameter Alias: N/A Parameter Validation: N/A .PARAMETER EventSource Specifies The Application Log Event Source To Be Used For The Error Event Logging. Parameter Set: All Parameter Alias: ScriptName Parameter Validation: Yes Using Value Existence Validation .PARAMETER PassThru Specifies That The CmdLet Have To Revert A Verification Collection. Parameter Set: All Parameter Alias: N/A Parameter Validation: N/A .INPUTS Pipeline Input None .OUTPUTS [System.Array] Which Contains A List With The On Premises Accepted Domain Objects. .NOTES AUTHOR: Svetoslav Nedyalkov Savov THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER. .EXAMPLE [System.Array]$arrAcceptedDomains = Connect-SnsExchangeOnPremises -UserName "CONTOSO\john.smith" ` -FolderPath "C:\" -PassThru; .EXAMPLE Connect-SnsExchangeOnPremises -FilePath "C:\CONTOSO@@john.smith.ini"; .EXAMPLE Connect-SnsExchangeOnPremises -Credential $objCredential; .EXAMPLE Connect-SnsExchangeOnPremises -Interactive; .LINK svesavov / SnsPsModule - https://github.com/svesavov/SnsPsModule .LINK Svetoslav Savov on LinkedIn - https://www.linkedin.com/in/svetoslavsavov #> [Alias('New-ExchangeOnPremSession')] [CmdletBinding(PositionalBinding = $false, DefaultParameterSetName = "Interactive")] Param ( [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateScript({(-not -not "$([Net.DNS]::GetHostEntry(""$($_)"").HostName)")})] [AllowNull()][AllowEmptyString()][System.String]$HostName, [Parameter(Mandatory = $true, ParameterSetName = "FolderPath", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateScript({(("$($_)".Contains("\")) -or ("$($_)" -match "$([SnsPsModule.SnsPatterns]::SnsSmtpAddressPattern)"))})] [ValidateNotNullOrEmpty()][System.String]$UserName, [Parameter(Mandatory = $true, ParameterSetName = "FolderPath", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateScript({("$($_)".Contains("\"))})] [ValidateScript({([System.IO.Directory]::Exists("$($_)"))})] [ValidateNotNullOrEmpty()][System.String]$FolderPath, [Parameter(Mandatory = $true, ParameterSetName = "FilePath", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [Alias("CredentialFile")] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateScript({("$($_)".EndsWith(".ini"))})] [ValidateScript({("$($_)".Contains("\"))})] [ValidateScript({([System.IO.File]::Exists("$($_)"))})] [ValidateNotNullOrEmpty()][System.String]$FilePath, [Parameter(Mandatory = $true, ParameterSetName = "Credential", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateNotNullOrEmpty()][System.Management.Automation.PSCredential]$Credential, [Parameter(Mandatory = $false, ParameterSetName = "Interactive", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [Switch]$Interactive = $false, [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateSet('https','http')] [ValidateNotNullOrEmpty()][System.String]$Protocol = 'https', [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [AllowNull()][AllowEmptyString()][System.String]$Prefix, [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateNotNullOrEmpty()][System.Int32]$Attempts = 3, [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [Alias("ScriptName")] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateScript({([SnsPsModule.SnsEventLog]::VerifySnsEventSource("$($_)"))})] [AllowNull()][AllowEmptyString()][System.String]$EventSource, [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [Switch]$PassThru ) ##### Override The Begin Method Begin { Write-Debug "Override Begin Method"; Write-Verbose ""; Write-Verbose "Connect-SnsExchangeOnPremises"; Write-Verbose "ParameterSetName: $($PSCmdlet.ParameterSetName)`r`n"; ##### Initialize New Measure Watch [System.Diagnostics.Stopwatch]$objCmdStopWatch = [System.Diagnostics.Stopwatch]::StartNew(); If ("$((Get-FileHash -Path ""$($PSCommandPath)"" -Algorithm ""SHA256"" -Verbose:$false -Debug:$false).Hash)" -ne "$($global:SnsModuleCfg.ModuleHash)") { Write-Warning "There Is New Version Of SnsPsModule Module Released. Please Restart The PowerShell Session." -WarningAction "Continue"; }; ##### Generate The Verbose Boolean Write-Debug "Generate The Verbose Boolean"; [System.Boolean]$bolVerbose = $false If ($PSCmdlet.MyInvocation.BoundParameters.Keys -icontains "Verbose") { [System.Boolean]$bolVerbose = $PSCmdlet.MyInvocation.BoundParameters.Verbose.IsPresent; } ##### Initialize The Variables [System.String]$strInitialVerboseSettings = "$($VerbosePreference)"; [System.Management.Automation.ScriptBlock]$scrBlock = $null; [System.Management.Automation.PSCredential]$objCredential = $null; [System.Int32]$intI = 0; [System.Management.Automation.PSModuleInfo]$objSesModule = $null; [System.Array]$arrAcceptedDomains = @(); ##### Generate The Verification Script Block [System.Management.Automation.ScriptBlock]$scrBlock = [System.Management.Automation.ScriptBlock]::Create("Get-$($Prefix)AcceptedDomain"); #================================================================================== #region Enumerate The Session Target #================================================================================== ##### Verify Whether HostName Is Not Provided Write-Debug "Verify Whether HostName Is Not Provided"; If (-not "$($HostName)") { [System.String]$HostName = Get-SnsNearestAliveCasServerHelper -Verbose:$bolVerbose -Debug:$false; } ##### Verify The Target Server Generation Write-Debug "Verify The Target Server Generation"; If (-not "$($HostName)") { [System.String]$strEventMessage = "There Is No Destination HostName Provided"; Log-SnsEventLogMessageHelper -Message "$($strEventMessage)" -EventLogEntryType "Error" -EventSource "$($EventSource)" -Verbose:$false -Debug:$false; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strEventMessage"; Return; } #================================================================================== #endregion Enumerate The Session Target #================================================================================== #================================================================================== #region Initialize The Credentials Object #================================================================================== ###### Verify The Parameter Set Name Write-Debug "Verify The Parameter Set Name"; [System.Management.Automation.PSCredential]$objCredential = $null; Switch ("$($PSCmdlet.ParameterSetName)") { "FilePath" { Write-Verbose "Import The Credential From A File"; [System.Management.Automation.PSCredential]$objCredential = Import-SnsCredentialFile -FilePath "$($FilePath)" -EventSource "$($EventSource)" -Verbose:$false -Debug:$false; If (-not (Test-SnsCredentialHelper -Credential $objCredential -Verbose:$bolVerbose -Debug:$false)) { [System.Management.Automation.PSCredential]$objCredential = $null; } Break; } "FolderPath" { Write-Verbose "Import The Credential From A File"; [System.Management.Automation.PSCredential]$objCredential = Import-SnsCredentialFile -UserName "$($UserName)" -FolderPath "$($FolderPath)" -EventSource "$($EventSource)" -Verbose:$false -Debug:$false; If (-not (Test-SnsCredentialHelper -Credential $objCredential -Verbose:$bolVerbose -Debug:$false)) { [System.Management.Automation.PSCredential]$objCredential = $null; } Break; } "Credential" { Write-Verbose "Assign The Provided Credential Object"; [System.Management.Automation.PSCredential]$objCredential = $Credential; If (-not (Test-SnsCredentialHelper -Credential $objCredential -Verbose:$bolVerbose -Debug:$false)) { [System.Management.Automation.PSCredential]$objCredential = $null; } Break; } } ##### Verify If It Is Interactive Session And There Are No Credentials Write-Debug "Verify If It Is Interactive Session And There Are No Credentials"; If ([System.Environment]::UserInteractive) { ##### Loop Interactive Credentials Dialog With The User Write-Debug "Loop Interactive Credentials Dialog With The User"; [System.Int32]$intI = 0; While ((-not "$($objCredential.UserName)") -and ($intI -lt $Attempts)) { ##### Ask The User About Credentials Write-Verbose "Ask The User About Credentials"; [System.Management.Automation.PSCredential]$objCredential = $null; [System.Management.Automation.PSCredential]$objCredential = Get-Credential -Verbose:$false -Debug:$false; ##### Verify The Provided Credentials Write-Verbose "Verify The Provided Credentials"; If (-not (Test-SnsCredentialHelper -Credential $objCredential -Verbose:$bolVerbose -Debug:$false)) { [System.Management.Automation.PSCredential]$objCredential = $null; } ##### Process The Loop Variable And TimeOut Start-Sleep -Seconds $(2 * $intI) -Verbose:$false -Debug:$false; [System.Int32]$intI = $intI + 1; } } ##### Verify The Credentials Object Write-Debug "Verify The Credentials Object"; If (-not "$($objCredential.UserName)") { [System.String]$strEventMessage = "Failed To Enumerate Exchange On Premises Credential"; Log-SnsEventLogMessageHelper -Message "$($strEventMessage)" -EventLogEntryType "Error" -EventSource "$($EventSource)" -Verbose:$false -Debug:$false; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strEventMessage"; Return; } #================================================================================== #endregion Initialize The Credentials Object #================================================================================== #================================================================================== #region Create Exchange On Premises Session #================================================================================== ##### Remove Any Previous Sessions Write-Debug "Remove Any Previous Sessions"; Remove-SnsPreviousSessionsHelper -Filter "$($HostName)" -ScrBlock $scrBlock -Verbose:$bolVerbose -Debug:$false; ##### Verify The Prerequisites For Remote PSSession Write-Debug "Verify The Prerequisites For Remote PSSession"; If ((-not -not "$($HostName)") -and (-not -not "$($objCredential.UserName)")) { ##### Verify The ParameterSetName Write-Debug "Verify The ParameterSetName"; $VerbosePreference = "SilentlyContinue"; [System.Management.Automation.PSModuleInfo]$objSesModule = $null; [System.Management.Automation.PSModuleInfo]$objSesModule = New-SnsRemoteSessionNoModuleHelper -Credential $objCredential -Url "$($Protocol)://$($HostName)/PowerShell/" ` -Prefix "$($Prefix)" -ProxyAccessType "NoProxyServer" -Attempts $Attempts -EventSource "$($EventSource)" -Verbose:$bolVerbose -Debug:$false; $VerbosePreference = "$($strInitialVerboseSettings)"; } #================================================================================== #endregion Create Exchange On Premises Session #================================================================================== #================================================================================== #region Retrieve The Accepted Domains Via The Remote Session #================================================================================== ##### Verify The PSSession Import Write-Debug "Verify The PSSession Import"; If (-not -not "$($objSesModule.Name)") { ##### Verify Whether Get-AcceptedDomain Command Is Among The Exported Commands Write-Debug "Verify Whether Get-AcceptedDomain Command Is Among The Exported Commands"; If ($objSesModule.ExportedCommands.Keys -icontains "Get-$($Prefix)AcceptedDomain") { ##### Generate The On Premises Accepted Domain Array Write-Debug "Generate The On Premises Accepted Domain Array"; [System.Array]$arrAcceptedDomains = @(); [System.Array]$arrAcceptedDomains = Invoke-Command -ScriptBlock $scrBlock -Verbose:$false -Debug:$false; ##### Verify The Exchange On Premises Session Import Write-Debug "Verify The Exchange On Premises Session Import"; If (($arrAcceptedDomains.Count) -eq 0) { [System.String]$strEventMessage = "Failed To Retrieve The Exchange On Premises AcceptedDomain"; Log-SnsEventLogMessageHelper -Message "$($strEventMessage)" -EventLogEntryType "Error" -EventSource "$($EventSource)" -Verbose:$false -Debug:$false; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strEventMessage"; Return; } } Else { Return; } } #================================================================================== #endregion Retrieve The Accepted Domains Via The Remote Session #================================================================================== ##### Reset The Variables Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "objSesModule"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "intI"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "objCredential"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "scrBlock"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strInitialVerboseSettings"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "bolVerbose"; } ##### Override The End Method End { Write-Debug "Override End Method"; Write-Verbose ""; ##### Stop The StopWatch $objCmdStopWatch.Stop(); Write-Verbose "Command Elapsed: ""$($objCmdStopWatch.ElapsedMilliseconds)"" Milliseconds." ; Write-Verbose "End!"; ##### Continue If Output Is Requested Write-Debug "Continue If Output Is Requested"; If (($PassThru.IsPresent) -and ($arrAcceptedDomains.Count -gt 0)) { ##### Pass The Output Object To The Pipeline Write-Debug "Pass Output Object To The Pipeline"; Return $arrAcceptedDomains } $PSCmdlet.MyInvocation.BoundParameters.Keys | ForEach { Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "$($_)"; } } } ##### Connect-SnsMsolService ====================================================== Function Connect-SnsMsolService () { <# .SYNOPSIS Establishes A Remote PowerShell Session To AzureAD MSOnline V1 Service. .DESCRIPTION Establishes A Remote PowerShell Session To AzureAD MSOnline V1 Service. The CmdLet Have Four Parameter Sets Depending On The Ways It Authenticates Against AzureAD: -- FolderPath Here Must Be Specified The UserName And The Full Absolute UNC Folder Path Where The Encrypted Password File Resides. -- FilePath Here Must Be Provided The Full Absolute UNC Path To The Credential File. The CmdLet Will Try To Enumerate The UserName From The FileName. -- Credential Here Must Be Provided System.Management.Automation.PSCredential Object. -- Interactive This Is The Only Parameter Set Which Is Capable To Establish Remote PowerShell Session To MSOnline V1 Service With Multifactor Authentication. However It Cannot Be Used Whenever The Script Or The Function Is Executed In As Service Mode. In This Parameter Set The CmdLet Opens A Window Where The User Can Specify His Credentials And Multi Factor Authentication Code Received On A SMS Or Inside A Phone App Or Phone Call And Etc. Obviously When The CmdLet Is Executed As Service There Is No Real Person To Specify The Credentials And The MFA Code. Depending On The Configuration There Might Not Be PowerShell Host Console Window Either. NOTE: The CmdLet Requires MSOnline Module To Be Installed In Advance. Please Refer To https://bit.ly/2O0VxwO NOTE: There Must Be A Direct Connection / Firewall Openings To Office 365. Proxy Usage Is Not Allowed. .PARAMETER UserName Specifies The UserName In UPN Format. Parameter Set: FolderPath Parameter Alias: N/A Parameter Validation: Yes Using RegEx Pattern Matching Validation .PARAMETER FolderPath Specifies The Full Absolute UNC Folder Path Where The Credential File Resides. Parameter Set: FolderPath Parameter Alias: N/A Parameter Validation: Yes Using Folder Existence Validation .PARAMETER FilePath Specifies The Full Absolute UNC Path To The Credential File. Parameter Set: FilePath Parameter Alias: CredentialFile Parameter Validation: Yes Using File Existence And File Extension Validation .PARAMETER Credential Specifies [System.Management.Automation.PSCredential] Object. Parameter Set: Credential Parameter Alias: N/A Parameter Validation: Yes Using Value Existence Validation .PARAMETER Interactive Specifies That The User Have To Be Asked Interactively For Credentials. Parameter Set: Interactive Parameter Alias: N/A Parameter Validation: N/A .PARAMETER Attempts Specifies The Number Of Attempts That Have To Be Made To Establish The Remote Session. Parameter Set: All Parameter Alias: N/A Parameter Validation: N/A .PARAMETER EventSource Specifies The Application Log Event Source To Be Used For The Error Event Logging. Parameter Set: All Parameter Alias: ScriptName Parameter Validation: Yes Using The EventSource Existence Validation .PARAMETER PassThru Specifies That The CmdLet Have To Revert A Verification Collection. Parameter Set: All Parameter Alias: N/A Parameter Validation: N/A .INPUTS Pipeline Input None .OUTPUTS [System.Array] Which Contains A List With The Available Msol License Objects. .NOTES AUTHOR: Svetoslav Nedyalkov Savov THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER. .EXAMPLE [System.Array]$arrAzureLicenses = Connect-SnsMsolService -UserName "john.smith@contoso.com" ` -FolderPath "C:\" -PassThru; .EXAMPLE Connect-SnsMsolService -FilePath "C:\john.smith@contoso.com.ini"; .EXAMPLE Connect-SnsMsolService -Credential $objCredential; .EXAMPLE Connect-SnsMsolService -Interactive; .LINK svesavov / SnsPsModule - https://github.com/svesavov/SnsPsModule .LINK Svetoslav Savov on LinkedIn - https://www.linkedin.com/in/svetoslavsavov #> [CmdletBinding(PositionalBinding = $false, DefaultParameterSetName = "Interactive")] Param ( [Parameter(Mandatory = $true, ParameterSetName = "FolderPath", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateScript({("$($_)" -match "$([SnsPsModule.SnsPatterns]::SnsSmtpAddressPattern)")})] [ValidateNotNullOrEmpty()][System.String]$UserName, [Parameter(Mandatory = $true, ParameterSetName = "FolderPath", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateScript({("$($_)".Contains("\"))})] [ValidateScript({([System.IO.Directory]::Exists("$($_)"))})] [ValidateNotNullOrEmpty()][System.String]$FolderPath, [Parameter(Mandatory = $true, ParameterSetName = "FilePath", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [Alias("CredentialFile")] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateScript({("$($_)".EndsWith(".ini"))})] [ValidateScript({("$($_)".Contains("\"))})] [ValidateScript({([System.IO.File]::Exists("$($_)"))})] [ValidateNotNullOrEmpty()][System.String]$FilePath, [Parameter(Mandatory = $true, ParameterSetName = "Credential", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateNotNullOrEmpty()][System.Management.Automation.PSCredential]$Credential, [Parameter(Mandatory = $false, ParameterSetName = "Interactive", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [Switch]$Interactive = $false, [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateNotNullOrEmpty()][System.Int32]$Attempts = 3, [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [Alias("ScriptName")] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateScript({([SnsPsModule.SnsEventLog]::VerifySnsEventSource("$($_)"))})] [AllowNull()][AllowEmptyString()][System.String]$EventSource, [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [Switch]$PassThru ) ##### Override The Begin Method Begin { Write-Debug "Override Begin Method"; Write-Verbose ""; Write-Verbose "Connect-SnsMsolService"; Write-Verbose "ParameterSetName: $($PSCmdlet.ParameterSetName)`r`n"; ##### Initialize New Measure Watch [System.Diagnostics.Stopwatch]$objCmdStopWatch = [System.Diagnostics.Stopwatch]::StartNew(); If ("$((Get-FileHash -Path ""$($PSCommandPath)"" -Algorithm ""SHA256"" -Verbose:$false -Debug:$false).Hash)" -ne "$($global:SnsModuleCfg.ModuleHash)") { Write-Warning "There Is New Version Of SnsPsModule Module Released. Please Restart The PowerShell Session." -WarningAction "Continue"; }; ##### Initialize The Variables [System.Management.Automation.PSCredential]$objCredential = $null; [System.Int32]$intI = 0; [System.Array]$arrClLics = @(); #================================================================================== #region Enumerate The Credentials Object #================================================================================== ##### Load MSOnline PowerShell Module Write-Debug "Load MSOnline PowerShell Module"; If (Import-SnsModuleHelper -Module "MSOnline" -EventSource "$($EventSource)") { Return; } ###### Generate The Credential Object In FolderPath Parameter Set Write-Debug "Verify The Parameter Set Name"; [System.Management.Automation.PSCredential]$objCredential = $null; Switch ("$($PSCmdlet.ParameterSetName)") { "FilePath" { Write-Verbose "Import The Credential Object"; [System.Management.Automation.PSCredential]$objCredential = Import-SnsCredentialFile -FilePath "$($FilePath)" -EventSource "$($EventSource)" -Verbose:$false -Debug:$false; Break; } "FolderPath" { Write-Verbose "Import The Credential Object"; [System.Management.Automation.PSCredential]$objCredential = Import-SnsCredentialFile -UserName "$($UserName)" -FolderPath "$($FolderPath)" -EventSource "$($EventSource)" -Verbose:$false -Debug:$false; Break; } "Credential" { Write-Verbose "Assign The Provided Credential Object"; [System.Management.Automation.PSCredential]$objCredential = $Credential; Break; } } ##### Verify The Credential Object Write-Debug "Verify The Credential Object"; If ((-not "$($objCredential.UserName)") -and (-not [System.Environment]::UserInteractive)) { [System.Management.Automation.PSCredential]$objCredential = $null; [System.String]$strEventMessage = "Failed To Enumerate The Msol V1 Service Credential"; Log-SnsEventLogMessageHelper -Message "$($strEventMessage)" -EventLogEntryType "Error" -EventSource "$($EventSource)" -Verbose:$false -Debug:$false; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strEventMessage"; Return; } #================================================================================== #endregion Enumerate The Credentials Object #================================================================================== #================================================================================== #region Establish Msol Service Connection #================================================================================== ##### Loop The Session Creation Write-Debug "Loop The Session Creation"; [System.Int32]$intI = 0; [System.Array]$arrClLics = @(); Do { ##### Verify The ParameterSetName Write-Debug "Verify The ParameterSetName"; If ((-not "$($objCredential.UserName)") -and [System.Environment]::UserInteractive) { ##### Display The Action On The Console Write-Verbose "Interactively Connecting To Msol V1 Service."; Connect-MsolService | Out-Null; } Else { If (-not -not "$($objCredential.UserName)") { ##### Display The Action On The Console Write-Verbose "Connecting To Msol V1 Service."; Connect-MsolService -Credential $objCredential | Out-Null; } Else { [System.Management.Automation.PSCredential]$objCredential = $null; [System.String]$strEventMessage = "Failed To Enumerate The Msol V1 Service Credential"; Log-SnsEventLogMessageHelper -Message "$($strEventMessage)" -EventLogEntryType "Error" -EventSource "$($EventSource)" -Verbose:$false -Debug:$false; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strEventMessage"; Return; } } ##### Process The Loop Variable and TimeOut Start-Sleep -Seconds $(2 * $intI) -Verbose:$false -Debug:$false; [System.Int32]$intI = $intI + 1; ##### Verify The PowerShell Session To The Msol Service [System.Array]$arrClLics = @(); [System.Array]$arrClLics = Get-MsolAccountSku -Verbose:$false -Debug:$false | ` Select-Object -Verbose:$false -Debug:$false @("AccountSkuId", "SkuId", "ServiceStatus", "ActiveUnits", "ConsumedUnits"); ##### } While (($arrClLics.Count -lt 1) -and ($intI -lt $Attempts)) ##### Verify The Msol V1 Service Session Creation Write-Debug "Verify The Msol V1 Service Session Creation"; If ($arrClLics.Count -lt 1) { [System.String]$strEventMessage = "Failed To Establish A Connection To Msol V1 Service"; Log-SnsEventLogMessageHelper -Message "$($strEventMessage)" -EventLogEntryType "Error" -EventSource "$($EventSource)" -Verbose:$false -Debug:$false; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strEventMessage"; Return; } Else { ##### Verify Whether The Global Msol V1 Licenses Variable Exists Write-Debug "Verify Whether The Global Msol V1 Licenses Variable Exists"; If (-not (Get-Variable -Verbose:$false -Debug:$false | Where-Object {"$($_.Name)" -like "ArrMsolLicenses"} -Verbose:$false -Debug:$false)) { ##### Initialize The Global Msol V1 Licenses Variable Write-Debug "Create The Global Msol V1 Licenses Variable"; New-Variable -Scope "Global" -Option "Constant" -Name "ArrMsolLicenses" -Value ($arrClLics); } } #================================================================================== #endregion Establish Msol Service Connection #================================================================================== ##### Reset The Variables Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "intI"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "objCredential"; } ##### Override The End Method End { Write-Debug "Override End Method"; Write-Verbose ""; ##### Stop The StopWatch $objCmdStopWatch.Stop(); Write-Verbose "Command Elapsed: ""$($objCmdStopWatch.ElapsedMilliseconds)"" Milliseconds." ; Write-Verbose "End!"; ##### Continue If Output Is Requested Write-Debug "Continue If Output Is Requested"; If (($PassThru.IsPresent) -and ($arrClLics.Count -gt 0)) { ##### Pass The Output Object To The Pipeline Write-Debug "Pass Output Object To The Pipeline"; Return $arrClLics } $PSCmdlet.MyInvocation.BoundParameters.Keys | ForEach { Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "$($_)"; } } } ##### Connect-SnsSharePointOnline ================================================= Function Connect-SnsSharePointOnline () { <# .SYNOPSIS Establishes Remote PowerShell Session To SharePoint Online In Office 365. .DESCRIPTION Establishes Remote PowerShell Session To SharePoint Online In Office 365. In Case The Session Creation Fail The CmdLet Can Log An Event In The Windows Event Viewer Application Log And Kill The Script Process. This Functionality Is Enabled Automatically When Value Is Provided To EventSource Parameter. The CmdLet Have Four Parameter Sets Depending On The Way That The CmdLet Authenticates Against The CAS Server: -- FolderPath Here Must Be Specified The UserName And The Full Absolute UNC Folder Path Where The Encrypted Password File Resides. -- FilePath Here Must Be Provided The Full Absolute UNC Path To The Credential File. The CmdLet Will Try To Enumerate The UserName From The FileName. -- Credential Here Must Be Provided System.Management.Automation.PSCredential Object. -- Interactive It Cannot Be Used Whenever The Script Or The Function Is Executed In As Service Mode. In This Parameter Set The CmdLet Opens A Window Where The User Can Specify His Credentials. Obviously When The CmdLet Is Executed As Service There Is No Real Person To Specify The Credentials. NOTE: The CmdLet Requires Microsoft.Online.SharePoint.PowerShell Module To Be Installed In Advance https://bit.ly/315AQoE NOTE: The CmdLet Requires The Host To Be Prepared For Remote PowerShell With Enable-PSRemoting And Then Disable-PSRemoting. The Host Does Not Require To Accept Remote Sessions. However Without Preparing And Then Removing The Remote Sessions Accepting The Generation Of Remote Sessions To Other Hosts Does Not Work As Well. .PARAMETER UserName Specifies The UserName. Parameter Set: FolderPath And Interactive Parameter Alias: N/A Parameter Validation: Yes Using RegEx Pattern Matching Validation .PARAMETER FolderPath Specifies The Full Absolute UNC Folder Path Where The Credential File Resides. Parameter Set: FolderPath Parameter Alias: N/A Parameter Validation: Yes Using Folder Existence Validation .PARAMETER FilePath Specifies The Full Absolute UNC Path To The Credential File. Parameter Set: FilePath Parameter Alias: CredentialFile Parameter Validation: Yes Using File Existence Validation .PARAMETER Credential Specifies [System.Management.Automation.PSCredential] Object. Parameter Set: Credential Parameter Alias: N/A Parameter Validation: Yes Using Value Existence Validation .PARAMETER Interactive Specifies That The User Have To Be Asked Interactively For Credentials. Parameter Set: Interactive Parameter Alias: N/A Parameter Validation: N/A .PARAMETER AuthenticationUrl Location For AAD Cross-Tenant Authentication Service. Can Be Optionally Used If Non-Default Cross-Tenant Authentication Service Is Used. Parameter Set: All Parameter Alias: N/A Parameter Validation: Yes Using DNS System Validation .PARAMETER ClientTag Permits Appending A Client Tag To Existing Client Tag. Used Optionally In The CSOM http Traffic To Identify Used Script Or Solution. Parameter Set: All Parameter Alias: N/A Parameter Validation: Yes Using Value Existence Validation .PARAMETER Region Specifies The Office 365 Regional Instances. Accepted Values: "Default", "ITAR", "Germany", "China" Parameter Set: All Parameter Alias: N/A Parameter Validation: Yes, Using Enumeration Validation. .PARAMETER TenantName Specifies The Office 365 Tenant Name To Which must Be Established A Connection. Parameter Set: All Parameter Alias: N/A Parameter Validation: Yes Using Value Existence Validation .PARAMETER Attempts Specifies The Number Of Attempts That Have To Be Made To Establish The Remote Session. Parameter Set: All Parameter Alias: N/A Parameter Validation: N/A .PARAMETER EventSource Specifies The Application Log Event Source To Be Used For The Error Event Logging. Parameter Set: All Parameter Alias: ScriptName Parameter Validation: Yes Using Value Existence Validation .PARAMETER PassThru Specifies That The CmdLet Have To Revert A Verification Collection. Parameter Set: All Parameter Alias: N/A Parameter Validation: N/A .INPUTS Pipeline Input None .OUTPUTS [System.Object] Which Contains SharePoint Tenant Object. .NOTES AUTHOR: Svetoslav Nedyalkov Savov THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER. .EXAMPLE [System.Object]$objSpTenant = Connect-SnsSharePointOnline -UserName "john.smith@contoso.com" ` -FolderPath "C:\" -PassThru; .EXAMPLE Connect-SnsSharePointOnline -FilePath "C:\john.smith@contoso.com.ini"; .EXAMPLE Connect-SnsSharePointOnline -Credential $objCredential; .EXAMPLE Connect-SnsSharePointOnline -Interactive; .LINK svesavov / SnsPsModule - https://github.com/svesavov/SnsPsModule .LINK Svetoslav Savov on LinkedIn - https://www.linkedin.com/in/svetoslavsavov #> [CmdletBinding(PositionalBinding = $false, DefaultParameterSetName = "Interactive")] Param ( [Parameter(Mandatory = $true, ParameterSetName = "FolderPath", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateScript({("$($_)" -match "$([SnsPsModule.SnsPatterns]::SnsSmtpAddressPattern)")})] [ValidateNotNullOrEmpty()][System.String]$UserName, [Parameter(Mandatory = $true, ParameterSetName = "FolderPath", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateScript({("$($_)".Contains("\"))})] [ValidateScript({([System.IO.Directory]::Exists("$($_)"))})] [ValidateNotNullOrEmpty()][System.String]$FolderPath, [Parameter(Mandatory = $true, ParameterSetName = "FilePath", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [Alias("CredentialFile")] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateScript({("$($_)".EndsWith(".ini"))})] [ValidateScript({("$($_)".Contains("\"))})] [ValidateScript({([System.IO.File]::Exists("$($_)"))})] [ValidateNotNullOrEmpty()][System.String]$FilePath, [Parameter(Mandatory = $true, ParameterSetName = "Credential", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateNotNullOrEmpty()][System.Management.Automation.PSCredential]$Credential, [Parameter(Mandatory = $false, ParameterSetName = "Interactive", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [Switch]$Interactive = $false, [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateScript({(-not -not "$([Net.DNS]::GetHostEntry(""$(""$($_)"".Split(""/"")[2])"").HostName)")})] [ValidateNotNullOrEmpty()][System.String]$AuthenticationUrl, [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateNotNullOrEmpty()][System.String]$ClientTag, [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateSet("Default", "ITAR", "Germany", "China")] [ValidateNotNullOrEmpty()][System.String]$Region = "Default", [Parameter(Mandatory = $true, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateNotNullOrEmpty()][System.String]$TenantName, [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateNotNullOrEmpty()][System.Int32]$Attempts = 3, [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [Alias("ScriptName")] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateScript({([SnsPsModule.SnsEventLog]::VerifySnsEventSource("$($_)"))})] [AllowNull()][AllowEmptyString()][System.String]$EventSource, [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [Switch]$PassThru ) ##### Override The Begin Method Begin { Write-Debug "Override Begin Method"; Write-Verbose ""; Write-Verbose "Connect-SnsSharePointOnline"; Write-Verbose "ParameterSetName: $($PSCmdlet.ParameterSetName)`r`n"; ##### Initialize New Measure Watch [System.Diagnostics.Stopwatch]$objCmdStopWatch = [System.Diagnostics.Stopwatch]::StartNew(); If ("$((Get-FileHash -Path ""$($PSCommandPath)"" -Algorithm ""SHA256"" -Verbose:$false -Debug:$false).Hash)" -ne "$($global:SnsModuleCfg.ModuleHash)") { Write-Warning "There Is New Version Of SnsPsModule Module Released. Please Restart The PowerShell Session." -WarningAction "Continue"; }; ##### Initialize The Variables [System.Management.Automation.ScriptBlock]$scrBlock = $null; [System.Management.Automation.PSCredential]$objCredential = $null; [System.Collections.Specialized.OrderedDictionary]$hshSplat = [Ordered]@{}; [System.Int32]$intI = 0; [System.Object]$objSpTenant = $null; ##### Initialize The Verification Script Block [System.Management.Automation.ScriptBlock]$scrBlock = [System.Management.Automation.ScriptBlock]::Create("Get-SPOTenant"); #================================================================================== #region Initialize The Credential Object #================================================================================== ##### Load Microsoft.Online.SharePoint.PowerShell Module Write-Debug "Load Microsoft.Online.SharePoint.PowerShell Module"; If (Import-SnsModuleHelper -Module "Microsoft.Online.SharePoint.PowerShell" -EventSource "$($EventSource)") { Return; } ###### Verify The Parameter Set Name Write-Debug "Verify The Parameter Set Name"; [System.Management.Automation.PSCredential]$objCredential = $null; Switch ("$($PSCmdlet.ParameterSetName)") { "FilePath" { Write-Verbose "Import The Credential From A File"; [System.Management.Automation.PSCredential]$objCredential = Import-SnsCredentialFile -FilePath "$($FilePath)" -EventSource "$($EventSource)" -Verbose:$false -Debug:$false; Break; } "FolderPath" { Write-Verbose "Import The Credential From A File"; [System.Management.Automation.PSCredential]$objCredential = Import-SnsCredentialFile -UserName "$($UserName)" -FolderPath "$($FolderPath)" -EventSource "$($EventSource)" -Verbose:$false -Debug:$false; Break; } "Credential" { Write-Verbose "Assign The Provided Credential Object"; [System.Management.Automation.PSCredential]$objCredential = $Credential; Break; } } ##### Verify The Credential Object Write-Debug "Verify The Credential Object"; If ((-not [System.Environment]::UserInteractive) -and (-not "$($objCredential.UserName)")) { [System.String]$strEventMessage = "Failed To Enumerate The SharePoint Online Credential"; Log-SnsEventLogMessageHelper -Message "$($strEventMessage)" -EventLogEntryType "Error" -EventSource "$($EventSource)" -Verbose:$false -Debug:$false; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strEventMessage"; Return; } #================================================================================== #endregion Initialize The Credential Object #================================================================================== #================================================================================== #region Create SharePoint Online Session #================================================================================== ##### Verify Any Previous Sessions Existence Write-Debug "Verify Any Previous Sessions Existence"; If (-not -not ( Get-Variable -Verbose:$false -Debug:$false | Where-Object -Verbose:$false -Debug:$false {"$($_.Name)" -like "ObjSpoTenant"})) { ##### Disconnect The Previous Sessions Write-Verbose "Removing Previous Sessions"; Disconnect-SPOService -Verbose:$false -Debug:$false | Out-Null; } ##### Prepare The Host Machine For Remote PSSession Write-Debug "Prepare The Host Machine For Remote PSSession"; If (-not (Prepare-SnsHostForRemoteSessions "$($EventSource)" -WhatIf:$false -Confirm:$false -Verbose:$false -Debug:$false)) { Return; } ##### Generate The Splatting HashTable Write-Debug "Generate The Splatting HashTable For New-CsOnlineSession"; [System.Collections.Specialized.OrderedDictionary]$hshSplat = [Ordered]@{}; If (-not -not "$($objCredential.UserName)") { $hshSplat.Add("Credential", $objCredential); } If (-not -not "$($AuthenticationUrl)") { $hshSplat.Add("AuthenticationUrl", "$($AuthenticationUrl)"); } If (-not -not "$($ClientTag)") { $hshSplat.Add("ClientTag", "$($ClientTag)"); } If (-not -not "$($Region)") { $hshSplat.Add("Region", "$($Region)"); } $hshSplat.Add("Url", "https://$($TenantName)-admin.sharepoint.com"); $hshSplat.Add("Verbose", $false); $hshSplat.Add("Debug", $false); ##### Loop The Session Creation Write-Debug "Loop The Session Creation"; [System.Int32]$intI = 0; [System.Object]$objSpTenant = $null; Do { ##### Establish The SharePoint Online Session Write-Verbose "Establish The SharePoint Online Session"; Connect-SPOService @hshSplat | Out-Null; ##### Process The Loop Variable And TimeOut Start-Sleep -Seconds $(2 * $intI) -Verbose:$false -Debug:$false; [System.Int32]$intI = $intI + 1; ##### Verify The Session Establishing [System.Object]$objSpTenant = $null; [System.Object]$objSpTenant = Invoke-Command -ScriptBlock $scrBlock -Verbose:$false -Debug:$false; } While ((-not "$($objSpTenant.SharingCapability)") -and ($intI -lt $Attempts)) ##### Verify Session Creation Write-Debug "Verify Session Creation"; If (-not "$($objSpTenant.SharingCapability)") { [System.String]$strEventMessage = "Failed To Establish PowerShell Session To SharePoint Online"; Log-SnsEventLogMessageHelper -Message "$($strEventMessage)" -EventLogEntryType "Error" -EventSource "$($EventSource)" -Verbose:$false -Debug:$false; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strEventMessage"; Return; } #================================================================================== #endregion Create SharePoint Online Session #================================================================================== #================================================================================== #region Generate The Output Objects #================================================================================== ##### Verify The PSSession Import Write-Debug "Verify The PSSession Import"; If (-not -not "$($objSpTenant.SharingCapability)") { ##### Verify Whether The Global SharePoint Online Tenant Variable Exists Write-Debug "Verify Whether The Global SharePoint Online Tenant Variable Exists"; If (-not (Get-Variable -Verbose:$false -Debug:$false | Where-Object {"$($_.Name)" -like "ObjSpoTenant"} -Verbose:$false -Debug:$false)) { ##### Create The Global SharePoint Online Tenant Variable Write-Debug "Create The Global SharePoint Online Tenant Variable"; New-Variable -Scope "Global" -Option "Constant" -Name "ObjSpoTenant" -Value ($objSpTenant); } } #================================================================================== #endregion Retrieve The SIP Domains Via The Remote Session #================================================================================== ##### Reset The Variables Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "intI"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "hshSplat"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "objCredential"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "scrBlock"; } ##### Override The End Method End { Write-Debug "Override End Method"; Write-Verbose ""; ##### Stop The StopWatch $objCmdStopWatch.Stop(); Write-Verbose "Command Elapsed: ""$($objCmdStopWatch.ElapsedMilliseconds)"" Milliseconds." ; Write-Verbose "End!"; ##### Continue If Output Is Requested Write-Debug "Continue If Output Is Requested"; If (($PassThru.IsPresent) -and (-not -not "$($objSpTenant.SharingCapability)")) { ##### Pass The Output Object To The Pipeline Write-Debug "Pass Output Object To The Pipeline"; Return $objSpTenant; } } } ##### Connect-SnsSkypeOnPremises ================================================== Function Connect-SnsSkypeOnPremises () { <# .SYNOPSIS Establishes Remote PowerShell Session To Skype For Business On Premises. .DESCRIPTION Establishes Remote PowerShell Session To Skype For Business On Premises. In Case The Session Creation Fail The CmdLet Can Log An Event In The Windows Event Viewer Application Log And Kill The Script Process. This Functionality Is Enabled Automatically When Value Is Provided To EventSource Parameter. The CmdLet Have Four Parameter Sets Depending On The Way That The CmdLet Authenticates Against The CAS Server: -- FolderPath Here Must Be Specified The UserName And The Full Absolute UNC Folder Path Where The Encrypted Password File Resides. -- FilePath Here Must Be Provided The Full Absolute UNC Path To The Credential File. The CmdLet Will Try To Enumerate The UserName From The FileName. -- Credential Here Must Be Provided System.Management.Automation.PSCredential Object. -- Interactive It Cannot Be Used Whenever The Script Or The Function Is Executed In As Service Mode. In This Parameter Set The CmdLet Opens A Window Where The User Can Specify His Credentials. Obviously When The CmdLet Is Executed As Service There Is No Real Person To Specify The Credentials. .PARAMETER Registrar Specifies The Fully Qualified Domain Name Of A Front End Server Or A Registrar Pool. It Will Be Used As PSSession Remote Host. As A Best Practice SBA Must Be Avoided Because They Cannot Manage The PSSession Load. If Omitted The CmdLet Will Try To Retrieve The FrontEnd Pools From AD And Chose The Nearest One. The Automatic Retrieval Works Only When The Skype Is Installed In The Root AD Domain. In Single Domain Forests It Always Works. Parameter Set: All Parameter Alias: N/A Parameter Validation: Yes Using DNS System Validation .PARAMETER UserName Specifies The UserName. Parameter Set: FolderPath Parameter Alias: N/A Parameter Validation: Validation: Yes Using RegEx Pattern Matching Validation .PARAMETER FolderPath Specifies The Full Absolute UNC Folder Path Where The Credential File Resides. Parameter Set: FolderPath Parameter Alias: N/A Parameter Validation: Yes Using Folder Existence Validation .PARAMETER FilePath Specifies The Full Absolute UNC Path To The Credential File. Parameter Set: FilePath Parameter Alias: CredentialFile Parameter Validation: Yes Using File Existence Validation .PARAMETER Credential Specifies [System.Management.Automation.PSCredential] Object. Parameter Set: Credential Parameter Alias: N/A Parameter Validation: Yes Using Impersonated LDAP Query Validation .PARAMETER Interactive Specifies That The User Have To Be Asked Interactively For Credentials. Parameter Set: Interactive Parameter Alias: N/A Parameter Validation: N/A .PARAMETER SkipSkypeModuleCheck Force The CmdLet To Skip The SkypeForBusiness PowerShell Module Existence Verification. If Omitted The CmdLet Will Try To Detect Whether SkypeForBusiness Module Is Installed And Imported. In Case It Is Installed And Not Imported The CmdLet Will Import It. Whenever SkypeForBusiness Module Is Available There Is No Need Of Skype On Premises Remote PSSession. The CmdLet Is Intended To Be Used Only When Skype For Business Management Tools Are Not Installed. Parameter Set: All Parameter Alias: N/A Parameter Validation: N/A .PARAMETER Protocol Specifies Whether HTTP Or HTTPS Protocol Shall Be Used. Parameter Set: All Parameter Alias: N/A Parameter Validation: Yes Using Enumeration Validation .PARAMETER Prefix Specifies The Prefix For The CmdLets In This Session. Parameter Set: All Parameter Alias: N/A Parameter Validation: Yes Using Value Existence Validation .PARAMETER Attempts Specifies The Number Of Attempts That Have To Be Made To Establish The Remote Session. Parameter Set: All Parameter Alias: N/A Parameter Validation: N/A .PARAMETER EventSource Specifies The Application Log Event Source To Be Used For The Error Event Logging. Parameter Set: All Parameter Alias: ScriptName Parameter Validation: Yes Using Value Existence Validation .PARAMETER PassThru Specifies That The CmdLet Have To Revert A Verification Collection. Parameter Set: All Parameter Alias: N/A Parameter Validation: N/A .INPUTS Pipeline Input None. .OUTPUTS [System.Array] Which Contains A List With The Skype For Business On Premises SIP Domain Objects. .NOTES AUTHOR: Svetoslav Nedyalkov Savov THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER. .EXAMPLE [System.Array]$arrSipDomains = Connect-SnsSkypeOnPremises -UserName "CONTOSO\john.smith" ` -FolderPath "C:\" -PassThru; .EXAMPLE Connect-SnsSkypeOnPremises -FilePath "C:\CONTOSO@@john.smith.ini"; .EXAMPLE Connect-SnsSkypeOnPremises -Credential $objCredential; .EXAMPLE Connect-SnsSkypeOnPremises -Interactive; .LINK svesavov / SnsPsModule - https://github.com/svesavov/SnsPsModule .LINK Svetoslav Savov on LinkedIn - https://www.linkedin.com/in/svetoslavsavov #> [Alias('New-SfbOnPremSession')] [CmdletBinding(PositionalBinding = $false, DefaultParameterSetName = "Interactive")] Param ( [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateScript({(-not -not "$([Net.DNS]::GetHostEntry(""$($_)"").HostName)")})] [AllowNull()][AllowEmptyString()][System.String]$Registrar, [Parameter(Mandatory = $true, ParameterSetName = "FolderPath", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateScript({(("$($_)".Contains("\")) -or ("$($_)" -match "$([SnsPsModule.SnsPatterns]::SnsSmtpAddressPattern)"))})] [ValidateNotNullOrEmpty()][System.String]$UserName, [Parameter(Mandatory = $true, ParameterSetName = "FolderPath", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateScript({("$($_)".Contains("\"))})] [ValidateScript({([System.IO.Directory]::Exists("$($_)"))})] [ValidateNotNullOrEmpty()][System.String]$FolderPath, [Parameter(Mandatory = $true, ParameterSetName = "FilePath", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [Alias("CredentialFile")] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateScript({("$($_)".EndsWith(".ini"))})] [ValidateScript({("$($_)".Contains("\"))})] [ValidateScript({([System.IO.File]::Exists("$($_)"))})] [ValidateNotNullOrEmpty()][System.String]$FilePath, [Parameter(Mandatory = $true, ParameterSetName = "Credential", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateNotNullOrEmpty()][System.Management.Automation.PSCredential]$Credential, [Parameter(Mandatory = $false, ParameterSetName = "Interactive", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [Switch]$Interactive = $false, [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [Switch]$SkipSkypeModuleCheck = $false, [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateSet("https", "http")] [ValidateNotNullOrEmpty()][System.String]$Protocol = "https", [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [AllowNull()][AllowEmptyString()][System.String]$Prefix, [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateNotNullOrEmpty()][System.Int32]$Attempts = 3, [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [Alias("ScriptName")] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateScript({([SnsPsModule.SnsEventLog]::VerifySnsEventSource("$($_)"))})] [AllowNull()][AllowEmptyString()][System.String]$EventSource, [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [Switch]$PassThru ) ##### Override The Begin Method Begin { Write-Debug "Override Begin Method"; Write-Verbose ""; Write-Verbose "Connect-SnsSkypeOnPremises"; Write-Verbose "ParameterSetName: $($PSCmdlet.ParameterSetName)`r`n"; ##### Initialize New Measure Watch [System.Diagnostics.Stopwatch]$objCmdStopWatch = [System.Diagnostics.Stopwatch]::StartNew(); If ("$((Get-FileHash -Path ""$($PSCommandPath)"" -Algorithm ""SHA256"" -Verbose:$false -Debug:$false).Hash)" -ne "$($global:SnsModuleCfg.ModuleHash)") { Write-Warning "There Is New Version Of SnsPsModule Module Released. Please Restart The PowerShell Session." -WarningAction "Continue"; }; ##### Generate The Verbose Boolean Write-Debug "Generate The Verbose Boolean"; [System.Boolean]$bolVerbose = $false If ($PSCmdlet.MyInvocation.BoundParameters.Keys -icontains "Verbose") { [System.Boolean]$bolVerbose = $PSCmdlet.MyInvocation.BoundParameters.Verbose.IsPresent; } ##### Initialize The Variables [System.String]$strInitialVerboseSettings = "$($VerbosePreference)"; [System.Net.NetworkInformation.Ping]$objPing = $null; [System.Management.Automation.ScriptBlock]$scrBlock = $null; [System.Array]$arrSipDomains = @(); [System.Array]$arrFrontEndPools = @(); [System.Management.Automation.PSCredential]$objCredential = $null; [System.Management.Automation.PSModuleInfo]$objSesModule = $null; ##### Generate The Ping Object Write-Debug "Generate The Ping Object"; [System.Net.NetworkInformation.Ping]$objPing = $null; [System.Net.NetworkInformation.Ping]$objPing = New-Object -TypeName 'System.Net.NetworkInformation.Ping' -Verbose:$false -Debug:$false; ##### Generate The Verification Script Block [System.Management.Automation.ScriptBlock]$scrBlock = $null; [System.Management.Automation.ScriptBlock]$scrBlock = [System.Management.Automation.ScriptBlock]::Create("Get-$($Prefix)CsSipDomain"); #================================================================================== #region Verify If The SkypeForBusiness Module Is Available And Loaded #================================================================================== If ((-not $SkipSkypeModuleCheck.IsPresent) -and (-not -not (Get-Module -Name "SkypeForBusiness" -Verbose:$false -Debug:$false))) { ##### Generate The Warning Message [System.String]$strEventMessage = ""; [System.String]$strEventMessage += "`r`n$(""*"" * 100)`r`n*$("" "" * 98)*`r`n"; [System.String]$strEventMessage += "*$("" "" * 27)Module ""SkypeForBusiness"" Is Already Loaded.$("" "" * 27)*`r`n"; [System.String]$strEventMessage += "*$("" "" * 98)*`r`n"; [System.String]$strEventMessage += "*$("" "" * 23)There Is No Need Of Remote SkypeForBusiness Session.$("" "" * 23)*`r`n"; [System.String]$strEventMessage += "*$("" "" * 98)*`r`n$(""*"" * 100)`r`n`r`n"; Log-SnsEventLogMessageHelper -Message "$($strEventMessage)" -EventLogEntryType "Warning" -EventSource "$($EventSource)" -Verbose:$false -Debug:$false; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strEventMessage"; Return (Get-CsSipDomain -Verbose:$false -Debug:$false) } ##### Verify SkypeForBusiness PowerShell Module Load Write-Debug "Verify SkypeForBusiness PowerShell Module Load"; If ((-not $SkipSkypeModuleCheck.IsPresent) -and (-not -not (Get-Module -Name "SkypeForBusiness" -ListAvailable:$true -Verbose:$false -Debug:$false))) { ##### Generate The Warning Message [System.String]$strEventMessage = ""; [System.String]$strEventMessage += "`r`n$(""*"" * 100)`r`n*$("" "" * 98)*`r`n"; [System.String]$strEventMessage += "*$("" "" * 20)Module ""SkypeForBusiness"" Is Already Present On The Server$("" "" * 20)*`r`n"; [System.String]$strEventMessage += "*$("" "" * 98)*`r`n"; [System.String]$strEventMessage += "*$("" "" * 34)Please Load The Module Instead$("" "" * 34)*`r`n"; [System.String]$strEventMessage += "*$("" "" * 98)*`r`n$(""*"" * 100)`r`n`r`n"; Log-SnsEventLogMessageHelper -Message "$($strEventMessage)" -EventLogEntryType "Warning" -EventSource "$($EventSource)" -Verbose:$false -Debug:$false; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strEventMessage"; ##### Import SkypeForBusiness PowerShell Module If (-not (Get-Module -Name "SkypeForBusiness" -Verbose:$false -Debug:$false)) { Write-Host "Import ""SkypeForBusiness"" PowerShell Module" -ForegroundColor "Green"; [System.String]$strVerbosePreference = "$($VerbosePreference)"; $VerbosePreference = "SilentlyContinue"; Import-Module -Name "SkypeForBusiness" -Global:$true -Force:$true -Verbose:$false -Debug:$false; $VerbosePreference = "$($strVerbosePreference)"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strVerbosePreference"; } ##### Generate The On Premises SIP Domains Array [System.Array]$arrSipDomains = @(); If (-not -not (Get-Module -Name "SkypeForBusiness" -Verbose:$false -Debug:$false)) { ##### Generate The On Premises SIP Domains Array Write-Debug "Generate The On Premises SIP Domains Array"; [System.Array]$arrSipDomains = Get-CsSipDomain -Verbose:$false -Debug:$false; } Return $arrSipDomains; } #================================================================================== #endregion Verify If The SkypeForBusiness Module Is Available And Loaded #================================================================================== #================================================================================== #region Generate The Session Target #================================================================================== ##### Verify Whether Registrar Is Provided Write-Debug "Verify Whether Registrar Is Provided"; If (-not "$($Registrar)") { ##### Retrieve Skype Registrar Pools From AD Write-Debug "Retrieve Skype Registrar Pools From AD"; [System.Array]$arrFrontEndPools = @(); [System.Array]$arrFrontEndPools = Search-SnsAdObject -DomainController "$(([ADSI]""LDAP://RootDSE"").dnsHostName)" ` -LdapQuery "(&(objectClass=msRTCSIP-Pool)(objectCategory=CN=ms-RTC-SIP-Pool,$(([ADSI]""LDAP://RootDSE"").schemaNamingContext))(msRTCSIP-PoolData=ExtendedType=CentralRegistrar))" ` -SearchRoot "$(([ADSI]""LDAP://RootDSE"").configurationNamingContext)" -ReturnProperties @("dNSHostName") -Verbose:$false -Debug:$false; ##### ##### Ping All FrontEnd Pools And Take The Nearest One Write-Debug "Ping All FrontEnd Pools And Take The Nearest One"; [System.String]$Registrar = $arrFrontEndPools | Select-Object -Property @{ "n" = "Fqdn"; "e" = {"$($_.Properties.dnshostname)"}} -Verbose:$false -Debug:$false | ` Select-Object -Verbose:$false -Debug:$false ` -Property @( "Fqdn", @{ "n" = "TTL"; "e" = {[System.Int32]"$($objPing.Send(""$($_.Fqdn)"", 100, [System.Text.Encoding]::ASCII.GetBytes(""S"")).Options.Ttl)"} } ) | ` Sort-Object -Property @( "Ttl", "Fqdn" ) -Descending:$true -Verbose:$false -Debug:$false | ` Select-Object -First 1 -Verbose:$false -Debug:$false | ` Select-Object -ExpandProperty "Fqdn" -Verbose:$false -Debug:$false; ##### } ##### Verify The Target Server Generation Write-Debug "Verify The Target Server Generation"; If (-not "$($Registrar)") { [System.String]$strEventMessage = "There Is No Destination Registrar Provided"; Log-SnsEventLogMessageHelper -Message "$($strEventMessage)" -EventLogEntryType "Error" -EventSource "$($EventSource)" -Verbose:$false -Debug:$false; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strEventMessage"; Return; } #================================================================================== #endregion Generate The Session Target #================================================================================== #================================================================================== #region Initialize The Credentials Object #================================================================================== ##### Verify The Parameter Set Name Write-Debug "Verify The Parameter Set Name"; [System.Management.Automation.PSCredential]$objCredential = $null; Switch ("$($PSCmdlet.ParameterSetName)") { "FilePath" { Write-Verbose "Import The Credential From A File"; [System.Management.Automation.PSCredential]$objCredential = Import-SnsCredentialFile -FilePath "$($FilePath)" -EventSource "$($EventSource)" -Verbose:$false -Debug:$false; If (-not (Test-SnsCredentialHelper -Credential $objCredential -Verbose:$bolVerbose -Debug:$false)) { [System.Management.Automation.PSCredential]$objCredential = $null; } Break; } "FolderPath" { Write-Verbose "Import The Credential From A File"; [System.Management.Automation.PSCredential]$objCredential = Import-SnsCredentialFile -UserName "$($UserName)" -FolderPath "$($FolderPath)" -EventSource "$($EventSource)" -Verbose:$false -Debug:$false; If (-not (Test-SnsCredentialHelper -Credential $objCredential -Verbose:$bolVerbose -Debug:$false)) { [System.Management.Automation.PSCredential]$objCredential = $null; } Break; } "Credential" { Write-Verbose "Assign The Provided Credential Object"; [System.Management.Automation.PSCredential]$objCredential = $Credential; If (-not (Test-SnsCredentialHelper -Credential $objCredential -Verbose:$bolVerbose -Debug:$false)) { [System.Management.Automation.PSCredential]$objCredential = $null; } Break; } } ##### Verify If It Is Interactive Session And There Are No Credentials Write-Debug "Verify If It Is Interactive Session And There Are No Credentials"; If ([System.Environment]::UserInteractive) { ##### Loop Interactive Credentials Dialog With The User Write-Debug "Loop Interactive Credentials Dialog With The User"; [System.Int32]$intI = 0; While ((-not "$($objCredential.UserName)") -and ($intI -lt $Attempts)) { ##### Ask The User About Credentials Write-Verbose "Ask The User About Credentials"; [System.Management.Automation.PSCredential]$objCredential = $null; [System.Management.Automation.PSCredential]$objCredential = Get-Credential -Verbose:$false -Debug:$false; ##### Verify The Provided Credentials Write-Verbose "Verify The Provided Credentials"; If (-not (Test-SnsCredentialHelper -Credential $objCredential -Verbose:$bolVerbose -Debug:$false)) { [System.Management.Automation.PSCredential]$objCredential = $null; } ##### Process The Loop Variable And TimeOut Start-Sleep -Seconds $(2 * $intI) -Verbose:$false -Debug:$false; [System.Int32]$intI = $intI + 1; } } ##### Verify The Credentials Object Write-Debug "Verify The Credentials Object"; If (-not "$($objCredential.UserName)") { [System.String]$strEventMessage = "Failed To Enumerate Skype On Premises Credential"; Log-SnsEventLogMessageHelper -Message "$($strEventMessage)" -EventLogEntryType "Error" -EventSource "$($EventSource)" -Verbose:$false -Debug:$false; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strEventMessage"; Return; } #================================================================================== #endregion Initialize The Credentials Object #================================================================================== #================================================================================== #region Establish SfB On Premises Session #================================================================================== ##### Remove Any Previous Sessions Write-Debug "Remove Any Previous Sessions"; Remove-SnsPreviousSessionsHelper -Filter "$($Registrar)" -ScrBlock $scrBlock -Verbose:$bolVerbose -Debug:$false; ##### Verify The Prerequisites For Remote PSSession Write-Debug "Verify The Prerequisites For Remote PSSession"; If ((-not -not "$($Registrar)") -and (-not -not "$($objCredential.UserName)")) { ##### Verify The ParameterSetName Write-Debug "Verify The ParameterSetName"; $VerbosePreference = "SilentlyContinue"; [System.Management.Automation.PSModuleInfo]$objSesModule = $null; [System.Management.Automation.PSModuleInfo]$objSesModule = New-SnsRemoteSessionNoModuleHelper -Credential $objCredential -Url "$($Protocol)://$($Registrar)/OcsPowershell/" ` -Prefix "$($Prefix)" -ProxyAccessType "NoProxyServer" -Attempts $Attempts -EventSource "$($EventSource)" -Verbose:$bolVerbose -Debug:$false; $VerbosePreference = "$($strInitialVerboseSettings)"; } #================================================================================== #endregion Establish SfB On Premises Session #================================================================================== #================================================================================== #region Retrieve The SIP Domains Via The Remote Session #================================================================================== ##### Verify The PSSession Import If (-not -not "$($objSesModule.Name)") { ##### Verify Whether Get-CsSipDomain Command Is Among The Exported Commands Write-Debug "Verify Whether Get-CsSipDomain Command Is Among The Exported Commands"; If ($objSesModule.ExportedCommands.Keys -icontains "Get-$($Prefix)CsSipDomain") { ##### Generate The On Premises SIP Domain Array Write-Debug "Generate The On Premises SIP Domain Array"; [System.Array]$arrSipDomains = @(); [System.Array]$arrSipDomains = Invoke-Command -ScriptBlock $scrBlock -Verbose:$false -Debug:$false; ##### Verify The SfB On Premises Session Import Write-Debug "Verify The SfB On Premises Session Import"; If (($arrSipDomains.Count) -eq 0) { [System.String]$strEventMessage = "Failed To Retrieve The On Premises CsSipDomain"; Log-SnsEventLogMessageHelper -Message "$($strEventMessage)" -EventLogEntryType "Error" -EventSource "$($EventSource)" -Verbose:$false -Debug:$false; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strEventMessage"; Return; } } Else { Return; } } #================================================================================== #endregion Retrieve The SIP Domains Via The Remote Session #================================================================================== ##### Reset The Variables Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "objSesModule"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "objCredential"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "arrFrontEndPools"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "scrBlock"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "objPing"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strInitialVerboseSettings"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "bolVerbose"; } ##### Override The End Method End { Write-Debug "Override End Method"; Write-Verbose ""; ##### Stop The StopWatch $objCmdStopWatch.Stop(); Write-Verbose "Command Elapsed: ""$($objCmdStopWatch.ElapsedMilliseconds)"" Milliseconds." ; Write-Verbose "End!"; ##### Continue If Output Is Requested Write-Debug "Continue If Output Is Requested"; If (($PassThru.IsPresent) -and ($arrSipDomains.Count -gt 0)) { ##### Pass The Output Object To The Pipeline Write-Debug "Pass Output Object To The Pipeline"; Return $arrSipDomains; } } } ##### Connect-SnsMicrosoftTeams =================================================== Function Connect-SnsMicrosoftTeams () { <# .SYNOPSIS Establishes Remote PowerShell Session To Microsoft Teams. .DESCRIPTION Establishes Remote PowerShell Session To Microsoft Teams. NOTE: The CmdLet Requires MicrosoftTeams PowerShell Module To Be Preinstalled. https://bit.ly/3PFWTdL WARNING: If Basic Authentication Is Not Enabled, Legacy *-Cs CmdLets Will Not Function Properly. For Remote PowerShell, Basic Authentication Is Necessary. https://bit.ly/3PLGSmR In Case The Session Creation Fail The CmdLet Can Log An Event In The Windows Event Viewer Application Log And Kill The Script Process. This Functionality Is Enabled Automatically When Value Is Provided To EventSource Parameter. The CmdLet Have Four Parameter Sets Depending On The Way That The CmdLet Authenticates Against Teams: -- FolderPath Here Must Be Specified The UserName And The Full Absolute UNC Folder Path Where The Encrypted Password File Resides. -- FilePath Here Must Be Provided The Full Absolute UNC Path To The Credential File. The CmdLet Will Try To Enumerate The UserName From The FileName. -- Credential Here Must Be Provided System.Management.Automation.PSCredential Object. -- Interactive It Cannot Be Used Whenever The Script Or The Function Is Executed In As Service Mode. In This Parameter Set The CmdLet Opens A Window Where The User Can Specify His Credentials. Obviously When The CmdLet Is Executed As Service There Is No Real Person To Specify The Credentials. .PARAMETER UserName Specifies The UserName. Parameter Set: FolderPath Parameter Alias: N/A Parameter Validation: Yes Using RegEx Pattern Matching Validation .PARAMETER ApplicationId Specifies The ApplicationId Of The AppRegistration Used For MicrosoftGraph Connection. Parameter Set: FolderPath Parameter Alias: ClientID Parameter Validation: Yes Using RegEx Pattern Matching Validation .PARAMETER FolderPath Specifies The Full Absolute UNC Folder Path Where The Credential Files Resides. Whenever The Connection Is Established Using Both MicrosoftTeams And MicrosoftGraph The Folder Must Contain The Credential Files For The UserPassword And The AppSecret. The CmdLet Will Try To Enumerate The Username And The ClientID From The Credential Files. Parameter Set: FolderPath Parameter Alias: N/A Parameter Validation: Yes Using Folder Existence Validation .PARAMETER FilePath Specifies The Full Absolute UNC Path To The User Credential File. Parameter Set: FilePath Parameter Alias: CredentialFile Parameter Validation: Yes Using File Existence Validation .PARAMETER ApplicationPasswordFilePath Specifies The Full Absolute UNC Path To The Application Secret File. Parameter Set: FilePath Parameter Alias: ClientPasswordFilePath Parameter Validation: Yes Using File Existence Validation .PARAMETER Credential Specifies [System.Management.Automation.PSCredential] Object For The User. Parameter Set: Credential Parameter Alias: N/A Parameter Validation: Yes Using Value Existence Validation .PARAMETER ApplicationCredential Specifies [System.Management.Automation.PSCredential] Object For The AppRegistration Secret. Parameter Set: Credential Parameter Alias: N/A Parameter Validation: Yes Using Value Existence Validation .PARAMETER Interactive Specifies That The User Have To Be Asked Interactively For Credentials. Parameter Set: Interactive Parameter Alias: N/A Parameter Validation: N/A .PARAMETER TenantID Specifies The TenantID GUID. Whenever The Connection Is Established Using Both MicrosoftTeams And MicrosoftGraph Becomes Mandatory. Parameter Set: All Parameter Alias: N/A Parameter Validation: Yes Using RegEx Pattern Matching Validation .PARAMETER Attempts Specifies The Number Of Attempts That Have To Be Made To Establish The Remote Session. Parameter Set: All Parameter Alias: N/A Parameter Validation: N/A .PARAMETER EventSource Specifies The Application Log Event Source To Be Used For The Error Event Logging. Parameter Set: All Parameter Alias: ScriptName Parameter Validation: Yes Using Value Existence Validation .PARAMETER PassThru Specifies That The CmdLet Have To Revert A Verification Collection. Parameter Set: All Parameter Alias: N/A Parameter Validation: N/A .INPUTS Pipeline Input None .OUTPUTS [System.Array] Which Contains A List With The Teams SIP Domain Objects. .NOTES AUTHOR: Svetoslav Nedyalkov Savov THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER. .EXAMPLE [System.Array]$arrSipDomains = Connect-SnsMicrosoftTeams -UserName "john.smith@contoso.com" ` -FolderPath "C:\" -PassThru; .EXAMPLE Connect-SnsMicrosoftTeams -UserName "john.smith@contoso.com" -FolderPath "C:\" ` -ApplicationId "00000000-0000-0000-0000-000000000000" -TenantID "00000000-0000-0000-0000-000000000000"; .EXAMPLE Connect-SnsMicrosoftTeams -FilePath "C:\john.smith@contoso.com.ini"; .EXAMPLE Connect-SnsMicrosoftTeams -FilePath "C:\john.smith@contoso.com.ini" ` -ApplicationPasswordFilePath "C:\00000000-0000-0000-0000-000000000000.ini" ` -TenantID "00000000-0000-0000-0000-000000000000"; .EXAMPLE Connect-SnsMicrosoftTeams -Credential $objCredential; .EXAMPLE Connect-SnsMicrosoftTeams -Credential $objCredential -ApplicationCredential $ApplicationCredential ` -TenantID "00000000-0000-0000-0000-000000000000"; .EXAMPLE Connect-SnsMicrosoftTeams -Interactive; .LINK svesavov / SnsPsModule - https://github.com/svesavov/SnsPsModule .LINK Svetoslav Savov on LinkedIn - https://www.linkedin.com/in/svetoslavsavov #> [Alias("New-SfbOnlineSession", "Connect-SnsSkypeOnline")] [CmdletBinding(PositionalBinding = $false, DefaultParameterSetName = "Interactive")] Param ( [Parameter(Mandatory = $true, ParameterSetName = "FolderPath", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateScript({("$($_)" -match "$([SnsPsModule.SnsPatterns]::SnsSmtpAddressPattern)")})] [ValidateNotNullOrEmpty()][System.String]$UserName, [Parameter(Mandatory = $false, ParameterSetName = "FolderPath", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [Alias("ClientID")] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateScript({("$($_)" -match "$([SnsPsModule.SnsPatterns]::GUIDPattern)")})] [ValidateNotNullOrEmpty()][System.String]$ApplicationId, [Parameter(Mandatory = $true, ParameterSetName = "FolderPath", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateScript({("$($_)".Contains("\"))})] [ValidateScript({([System.IO.Directory]::Exists("$($_)"))})] [ValidateNotNullOrEmpty()][System.String]$FolderPath, [Parameter(Mandatory = $true, ParameterSetName = "FilePath", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [Alias("CredentialFile")] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateScript({("$($_)".EndsWith(".ini"))})] [ValidateScript({("$($_)".Contains("\"))})] [ValidateScript({([System.IO.File]::Exists("$($_)"))})] [ValidateNotNullOrEmpty()][System.String]$FilePath, [Parameter(Mandatory = $false, ParameterSetName = "FilePath", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [Alias("ClientPasswordFilePath")] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateScript({("$($_)".EndsWith(".ini"))})] [ValidateScript({("$($_)".Contains("\"))})] [ValidateScript({([System.IO.File]::Exists("$($_)"))})] [ValidateNotNullOrEmpty()][System.String]$ApplicationPasswordFilePath, [Parameter(Mandatory = $true, ParameterSetName = "Credential", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateNotNullOrEmpty()][System.Management.Automation.PSCredential]$Credential, [Parameter(Mandatory = $false, ParameterSetName = "Credential", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateNotNullOrEmpty()][System.Management.Automation.PSCredential]$ApplicationCredential, [Parameter(Mandatory = $false, ParameterSetName = "Interactive", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [Switch]$Interactive = $false, [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateScript({("$($_)" -match "$([SnsPsModule.SnsPatterns]::GUIDPattern)")})] [ValidateNotNullOrEmpty()][System.String]$TenantID, [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateNotNullOrEmpty()][System.Int32]$Attempts = 3, [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [Alias("ScriptName")] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateScript({([SnsPsModule.SnsEventLog]::VerifySnsEventSource("$($_)"))})] [AllowNull()][AllowEmptyString()][System.String]$EventSource, [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [Switch]$PassThru ) ##### Override The Begin Method Begin { Write-Debug "Override Begin Method"; Write-Verbose ""; Write-Verbose "Connect-SnsMicrosoftTeams"; Write-Verbose "ParameterSetName: $($PSCmdlet.ParameterSetName)`r`n"; ##### Initialize New Measure Watch [System.Diagnostics.Stopwatch]$objCmdStopWatch = [System.Diagnostics.Stopwatch]::StartNew(); If ("$((Get-FileHash -Path ""$($PSCommandPath)"" -Algorithm ""SHA256"" -Verbose:$false -Debug:$false).Hash)" -ne "$($global:SnsModuleCfg.ModuleHash)") { Write-Warning "There Is New Version Of SnsPsModule Module Released. Please Restart The PowerShell Session." -WarningAction "Continue"; }; ##### Initialize The Variables [System.Management.Automation.PSCredential]$objCredential = $null; [System.Management.Automation.PSCredential]$objAppCredential = $null; [System.Collections.Specialized.OrderedDictionary]$hshSplat = [Ordered]@{}; [System.Object]$objVerif = $null; [System.Int32]$intI = 0; [System.Array]$arrSipDomains = @(); #================================================================================== #region Initialize The Credentials Object #================================================================================== ##### Load MicrosoftTeams PowerShell Module Write-Debug "Load MicrosoftTeams PowerShell Module"; If (Import-SnsModuleHelper -Module "MicrosoftTeams" -EventSource "$($EventSource)") { Return; } ###### Verify The ParameterSetName Write-Debug "Verify The ParameterSetName"; [System.Management.Automation.PSCredential]$objCredential = $null; [System.Management.Automation.PSCredential]$objAppCredential = $null; Switch ("$($PSCmdlet.ParameterSetName)") { "FilePath" { Write-Verbose "Import The Credential From A File"; [System.Management.Automation.PSCredential]$objCredential = Import-SnsCredentialFile -FilePath "$($FilePath)" -EventSource "$($EventSource)" -Verbose:$false -Debug:$false; If (-not -not "$($ApplicationPasswordFilePath)") { Write-Verbose "Import The Application Secret From A File"; [System.Management.Automation.PSCredential]$objAppCredential = Import-SnsCredentialFile -FilePath "$($ApplicationPasswordFilePath)" -EventSource "$($EventSource)" -Verbose:$false -Debug:$false; } Break; } "FolderPath" { Write-Verbose "Import The Credential From A File"; [System.Management.Automation.PSCredential]$objCredential = Import-SnsCredentialFile -UserName "$($UserName)" -FolderPath "$($FolderPath)" -EventSource "$($EventSource)" -Verbose:$false -Debug:$false; If (-not -not "$($ApplicationId)") { Write-Verbose "Import The Application Secret From A File"; [System.Management.Automation.PSCredential]$objAppCredential = Import-SnsCredentialFile -UserName "$($ApplicationId)" -FolderPath "$($FolderPath)" -EventSource "$($EventSource)" -Verbose:$false -Debug:$false; } Break; } "Credential" { Write-Verbose "Assign The Provided Credential Object"; [System.Management.Automation.PSCredential]$objCredential = $Credential; If (-not -not "$($ApplicationCredential.UserName)") { Write-Verbose "Assign The Provided Application Secret Credential Object"; [System.Management.Automation.PSCredential]$objAppCredential = $ApplicationCredential } Break; } } ##### Verify The Credential Object Write-Debug "Verify The Credential Object"; If ((-not "$($objCredential.UserName)") -and (-not [System.Environment]::UserInteractive)) { [System.Management.Automation.PSCredential]$objCredential = $null; [System.Management.Automation.PSCredential]$objAppCredential = $null; [System.String]$strEventMessage = "Failed To Enumerate The Credential For Teams"; Log-SnsEventLogMessageHelper -Message "$($strEventMessage)" -EventLogEntryType "Error" -EventSource "$($EventSource)" -Verbose:$false -Debug:$false; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strEventMessage"; Return; } ##### Verify The TenantID Object Write-Debug "Verify The TenantID Object"; If ((-not -not "$($objAppCredential.UserName)") -and (-not "$($TenantID)")) { [System.Management.Automation.PSCredential]$objCredential = $null; [System.Management.Automation.PSCredential]$objAppCredential = $null; [System.String]$strEventMessage = "TenantID Is Required When AccessTokens Authentication Is Used"; Log-SnsEventLogMessageHelper -Message "$($strEventMessage)" -EventLogEntryType "Error" -EventSource "$($EventSource)" -Verbose:$false -Debug:$false; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strEventMessage"; Return; } #================================================================================== #endregion Initialize The Credentials Object #================================================================================== #================================================================================== #region Establish MicrosoftTeams Connection #================================================================================== ##### Initialize The Splatting HashTable Write-Debug "Initialize The Splatting HashTable"; [System.Collections.Specialized.OrderedDictionary]$hshSplat = [Ordered]@{}; $hshSplat.Add("WhatIf", $false); $hshSplat.Add("Confirm", $false); $hshSplat.Add("Verbose", $false); $hshSplat.Add("Debug", $false); If ((-not -not "$($TenantID)") -and (-not "$($objAppCredential.UserName)")) { $hshSplat.Add("TenantID", "$($TenantID)"); } ##### Verify Whether We Have User Credential Object Write-Debug "Verify Whether We Have User Credential Object"; If (-not -not "$($objCredential.UserName)") { ##### Verify Whether We Have App Credential Object Write-Debug "Verify Whether We Have App Credential Object"; If (-not -not "$($objAppCredential.UserName)") { #================================================================================== #region Authenticate Against MicrosoftGraph And MicrosoftTeams #================================================================================== ##### Initialize The Tokens Splatting HashTable Write-Debug "Initialize The Tokens Splatting HashTable"; [System.Collections.Specialized.OrderedDictionary]$hshTokenSplat = [Ordered]@{}; $hshTokenSplat.Add("URI", "https://login.microsoftonline.com/$($TenantID)/oauth2/v2.0/token"); $hshTokenSplat.Add("Method", "POST"); $hshTokenSplat.Add("ContentType", "application/x-www-form-urlencoded"); $hshTokenSplat.Add("Verbose", $false); $hshTokenSplat.Add("Debug", $false); ##### Initialize The Rest Request Body Write-Debug "Initialize The Rest Request Body"; [System.String]$strBody = ""; [System.String]$strBody = "client_id=$($objAppCredential.UserName)&client_secret=$( ` [Net.WebUtility]::URLEncode(""$($objAppCredential.GetNetworkCredential().Password)"") ` )&grant_type=password&username=$($objCredential.UserName)&password=$( ` [Net.WebUtility]::URLEncode(""$($objCredential.GetNetworkCredential().Password)""))"; ##### ##### Loop The MicrosoftGraph Authentication Write-Debug "Loop The MicrosoftGraph Authentication"; [System.Int32]$intI = 0; [System.String]$strGraphToken = ""; While ((-not "$($strGraphToken)") -and ($intI -lt $Attempts)) { ##### Authenticate Against MicrosoftGraph Write-Verbose "Authenticate Against MicrosoftGraph"; [System.String]$strGraphToken = ""; [System.String]$strGraphToken = "$((Invoke-RestMethod @hshTokenSplat -Body ""$($strBody)&scope=https://graph.microsoft.com/.default"").access_token)"; Start-Sleep -Seconds $(2 * $intI) -Verbose:$false -Debug:$false; [System.Int32]$intI = $intI + 1; } ##### Verify The MicrosoftGraph Authentication Write-Debug "Verify The MicrosoftGraph Authentication"; If (-not "$($strGraphToken)") { [System.String]$strEventMessage = "Failed To Authenticate Against MicrosoftGraph"; Log-SnsEventLogMessageHelper -Message "$($strEventMessage)" -EventLogEntryType "Error" -EventSource "$($EventSource)" -Verbose:$false -Debug:$false; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strEventMessage"; Return; } ##### Loop The MicrosoftTeams Authentication Write-Debug "Loop The MicrosoftTeams Authentication"; [System.Int32]$intI = 0; [System.String]$strTeamsToken = ""; While ((-not "$($strTeamsToken)") -and ($intI -lt $Attempts)) { ##### Authenticate Against MicrosoftTeams Write-Verbose "Authenticate Against MicrosoftTeams"; [System.String]$strTeamsToken = ""; [System.String]$strTeamsToken = "$((Invoke-RestMethod @hshTokenSplat -Body ""$($strBody)&scope=48ac35b8-9aa8-4d74-927d-1f4a14a0b239/.default"").access_token)"; Start-Sleep -Seconds $(2 * $intI) -Verbose:$false -Debug:$false; [System.Int32]$intI = $intI + 1; } ##### Verify The MicrosoftTeams Authentication Write-Debug "Verify The MicrosoftTeams Authentication"; If (-not "$($strTeamsToken)") { [System.String]$strEventMessage = "Failed To Authenticate Against MicrosoftTeams"; Log-SnsEventLogMessageHelper -Message "$($strEventMessage)" -EventLogEntryType "Error" -EventSource "$($EventSource)" -Verbose:$false -Debug:$false; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strEventMessage"; Return; } ##### Add The AccessTokens To Connect-MicrosoftTeams Splatting HashTable Write-Debug "Add The AccessTokens To Connect-MicrosoftTeams Splatting HashTable"; $hshSplat.Add("AccessTokens", @("$($strGraphToken)", "$($strTeamsToken)")); [ValidateNotNullOrEmpty()][System.Int32]$Attempts = 1; ##### Reset The Variables Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strTeamsToken"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strGraphToken"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strBody"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "hshTokenSplat"; #================================================================================== #endregion Authenticate Against MicrosoftGraph And MicrosoftTeams #================================================================================== } Else { $hshSplat.Add("Credential", $objCredential); } } ##### Loop The Session Initialization Write-Debug "Loop The Session Initialization"; [System.Int32]$intI = 0; [Microsoft.TeamsCmdlets.Powershell.Connect.Models.PSAzureContext]$objVerif = $null; While ((-not "$($objVerif.TenantId.Guid)") -and ($intI -lt $Attempts)) { ##### Connecting To MicrosoftTeams Write-Verbose "Connecting To MicrosoftTeams"; [Microsoft.TeamsCmdlets.Powershell.Connect.Models.PSAzureContext]$objVerif = $null; [Microsoft.TeamsCmdlets.Powershell.Connect.Models.PSAzureContext]$objVerif = Connect-MicrosoftTeams @hshSplat; ##### Process The Loop Variable and TimeOut Start-Sleep -Seconds $(2 * $intI) -Verbose:$false -Debug:$false; [System.Int32]$intI = $intI + 1; } ##### Verify The MicrosoftTeams Session Creation Write-Debug "Verify The MicrosoftTeams Session Creation"; If (-not "$($objVerif.TenantId.Guid)") { [System.String]$strEventMessage = "Failed To Establish MicrosoftTeams Connection"; Log-SnsEventLogMessageHelper -Message "$($strEventMessage)" -EventLogEntryType "Error" -EventSource "$($EventSource)" -Verbose:$false -Debug:$false; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strEventMessage"; Return; } #================================================================================== #endregion Establish MicrosoftTeams Connection #================================================================================== #================================================================================== #region Retrieve The SIP Domains Via The Remote Session #================================================================================== ##### Verify The PSSession Import Write-Debug "Verify The PSSession Import"; If (-not -not "$($objVerif.TenantId.Guid)") { ##### Generate The SfB Online SIP Domain Array Write-Debug "Generate The SfB Online SIP Domain Array"; [System.Array]$arrSipDomains = @(); [System.Array]$arrSipDomains = Get-CsOnlineSipDomain -Verbose:$false -Debug:$false; ##### Verify The SfB Online Session Import Write-Debug "Verify The SfB Online Session Import"; If (($arrSipDomains.Count) -eq 0) { [System.String]$strEventMessage = "Failed To Retrieve The CsOnlineSipDomain"; Log-SnsEventLogMessageHelper -Message "$($strEventMessage)" -EventLogEntryType "Error" -EventSource "$($EventSource)" -Verbose:$false -Debug:$false; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strEventMessage"; Return; } } #================================================================================== #endregion Retrieve The SIP Domains Via The Remote Session #================================================================================== ##### Reset The Variables Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "intI"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "objVerif"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "hshSplat"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "objAppCredential"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "objCredential"; } ##### Override The End Method End { Write-Debug "Override End Method"; Write-Verbose ""; ##### Stop The StopWatch $objCmdStopWatch.Stop(); Write-Verbose "Command Elapsed: ""$($objCmdStopWatch.ElapsedMilliseconds)"" Milliseconds." ; Write-Verbose "End!"; ##### Continue If Output Is Requested Write-Debug "Continue If Output Is Requested"; If (($PassThru.IsPresent) -and ($arrSipDomains.Count -gt 0)) { ##### Pass The Output Object To The Pipeline Write-Debug "Pass Output Object To The Pipeline"; Return $arrSipDomains; } } } ##### Convert-SnsObjectToSQLInsertQuery =========================================== Function Convert-SnsObjectToSQLInsertQuery () { <# .SYNOPSIS This Cmdlet Convert A Specified Custom Object Or Import Custom Object To A SQL Insert Query And Either Revert The SQL Query As String Collection Or Actually Insert The Objects Into The Specified SQL DataBase If All The Prerequisites Are Met. .DESCRIPTION This Cmdlet Convert A Specified Custom Object Or Import Custom Object To A SQL Insert Query And Either Revert The SQL Query As String Collection Or Actually Insert The Objects Into The Specified SQL DataBase If All The Prerequisites Are Met. The CmdLet Have Two Parameter Sets, Related With The Way That The Input Objects Are Provided: -- InputObject Parameter Set Is Used Whenever Are Provided Custom Objects As Input To The CmdLet. The InputObjects Can Be Generated With The Script That Is Calling The CmdLet Or Any Other Way. The Input Object Must Have String Property Values Or Values Which Are Convertible To String. Multivalued String Properties Or Multivalued Properties Which Can Be Converted To A String Can Be Provided, However They Will Be Converted To A Single String Using .NET Sytem.String Join Method. For Separator Will Be Used The Value Of The CmdLet SqlDelimiter Parameter. Whenever The Parameter Is Not Specified Will Be Used Tilde (~) Character. -- FileImport Parameter Set Is Used Whenever The InputObjects Have To Be Imported From A .CSV File. For That Purpose The CmdLet Calls Internally Import-Csv CmdLet. In That Way Using Single CmdLet Can Be Imported .CSV Files, Converted To SQL Insert Queries And Actually Inserted Into The Required DataBase. All Restrictions, Advantages And Specifics Of Import-Csv CmdLet Applies Here As Well. The CmdLet Can Actually Insert The Generated SQL Queries Into SQL DataBase. For That Purpose The User Have To Specify The SQL Server And The Way The SQL Connection Is Authenticated. The DatabaseName And DatabaseTable Parameters Are Required Parameters In All Parameter Sets Because They Are Used Within The SQL Queries. With Providing The SQL Server And The Authentication Mechanism The CmdLet Actually Inserts The Queries. The Ways That The CmdLet Can Use For SQL Authentication Are Via Providing Of Credential PowerShell Object, Via Providing UserName And Password In Clear Text As Parameters, Or Using The Current Session Of The User Who Executes The CmdLet. For That Purpose The Actual Authentication Used Depends Of The Combination Of Parameters Provided To The CmdLet. When Credentials Parameter Is Used The Authentication Is Performed Using It, The Rest Of The Authentication Parameters Can Be Not Specified In That Case Whenever They Are Specified Will Be Ignored. When No Credentials Object Is Provided And Both UserName And Password Are Provided In Clear Text As Parameter They Will Be Used. The Connection From .NET SQL Client To The SQL Server Is Encrypted, Therefore Even Provided In Clear Text To The CmdLet The Credentials Cannot Be Intercepted. When No Credential Object Is Provided And When No UserName And Password Are Provided Or Incompletely Provided, The CmdLet Will Connect To The SQL Server Using The Single Sign On And The Session Of The Currently Logged User. This Is The Most Appropriate And Recommended When The CmdLet Is Used In Scripts Run By A Person On Run Via Scheduled Task By User Who Have The Needed SQL Access Rights. Whenever Actual Insert Into The SQL DataBase Is Used The CmdLet Will Retrieve The Actual Column Names From The Destination SQL Table, And Will Automatically Filter Out Any Auto Increment Columns. Afterward It Will Select Only The Properties From The InputObject That Match The Column Names. In Case The Column Names Does Not Match The Corresponding SQL Columns Will Have NULL Value In The Generated SQL Queries. Going One Step Further The CmdLet Will Verify Whether All Not Nullable Columns Have Values. From That Perspective If The Input Object Properties Are Misspelled At Least The Mandatory Columns Will Be Verified. In Case Actual Insert Is Not Used, The CmdLet Have No SQL Connection Information And Cannot Retrieve The Actual Columns Of The Table. In That Case The SQL Queries Are Generated As Is Using The InputObject Properties. It Is User Responsibility If The Property Names Are Misspelled. .PARAMETER InputObject Specifies An Object Collection Which Have To Be Converted To SQL Queries. Parameter Set: InputObject Parameter Alias: N/A Parameter Validation: N/A .PARAMETER Path Specifies The Path To The CSV File To Import. Parameter Set: FileImport Parameter Alias: N/A Parameter Validation: Yes, Using Path Validation .PARAMETER Delimiter Specifies The Delimiter That Separates The Property Values In The CSV File. The Default Is A Comma (,). Enter A Character, Such As A Colon (:). To Specify A Semicolon (;), Enclose It In Quotation Marks. If You Specify A Character Other Than The Actual String Delimiter In The File, Import-Csv Cannot Create Objects From The CSV Strings. Instead, It Returns The Strings. Parameter Set: FileImport Parameter Alias: N/A Parameter Validation: N/A .PARAMETER Encoding Specifies The Type Of Character Encoding That Was Used In The CSV File. The Acceptable Values For This Parameter Are: - Unicode - UTF7 - UTF8 - ASCII - UTF32 - BigEndianUnicode - Default - OEM The default is ASCII. Parameter Set: FileImport Parameter Alias: N/A Parameter Validation: Validation Using Enumeration .PARAMETER Header Specifies An Alternate Column Header Row For The Imported File. The Column Header Determines The Names Of The Properties Of The Object That Import-Csv Creates. Enter A Comma-Separated List Of The Column Headers. Enclose Each Item In Quotation Marks (Single Or Double). Do Not Enclose The Header String In Quotation Marks. If You Enter Fewer Column Headers Than There Are Columns, The Remaining Columns Will Have No Header. If You Enter More Headers Than There Are Columns, The Extra Headers Are Ignored. When Using The Header Parameter, Delete The Original Header Row From The CSV File. Otherwise, Import-Csv Creates An Extra Object From The Items In The Header Row. Parameter Set: FileImport Parameter Alias: N/A Parameter Validation: N/A .PARAMETER Computer Specifies A SQL Server And SQL Instance In Format: <DataBase Server> <DataBase Server>\<DataBase Instance> <DataBase Server>,<Port Number> When The Parameter Is Not Provided The CmdLet Will Skip The Input Objects Verifications And Will Not Insert The Generated Queries Into The DataBase. Instead Will Revert The Generated SQL Queries As Output String Collection. Parameter Set: All Parameter Alias: ComputerName, SqlServer, SqlServerName, DbServer, DbServerName, Server, ServerName Parameter Validation: Yes, Using DNS Resolution .PARAMETER DatabaseName Specifies A DataBase Name Parameter Set: All Parameter Alias: DbName, DataBase, SqlDatabaseName, SqlDbName, SqlDb Parameter Validation: N/A .PARAMETER DatabaseTable Specifies A DataBase Table Name Used To Insert In The Objects The CmdLet Will Expect The Full Table Name Along With The DataBase Schema For Example [<dbo>].[<TableName>] The Square Brackets Are Not Required They Are Characters Used To Close The Names Whenever They Contain Spaces Parameter Set: All Parameter Alias: TableName, Table, SqlTableName Parameter Validation: Yes Using Syntax Validation .PARAMETER UserName Specifies A UserName In Clear Text Parameter Set: All Parameter Alias: SqlUser, User, Usr, UID, DbUser Parameter Validation: N/A .PARAMETER Password Specifies A Password In Clear Text Parameter Set: All Parameter Alias: SqlPassword, DbPassword, pwd, Pass Parameter Validation: N/A .PARAMETER Credentials Specifies A [System.Management.Automation.PSCredential] Object Parameter Set: All Parameter Alias: SqlCredentials, SqlCred, Cred Parameter Validation: N/A .PARAMETER SqlDelimiter Specifies The Character Used To Join A Collection Of Strings In Multivalued Object Property To A String Value Required For SQL Cell Object. Parameter Set: InputObject Parameter Alias: N/A Parameter Validation: N/A .PARAMETER PassThru Specifies That The CmdLet Have To Revert A Verification Boolean In Case Of Actual SQL Insert. If No SQL Insert Is Performed The Parameter Have No Impact. Parameter Set: All Parameter Alias: N/A Parameter Validation: N/A .INPUTS Pipeline Input -InputObject [System.Array] .OUTPUTS Pipeline Output [System.String[]] Which Contains The Generated SQL Queries .NOTES AUTHOR: Svetoslav Nedyalkov Savov THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER. .EXAMPLE [System.String[]]$arrSqlQry = Convert-SnsObjectToSQLInsertQuery -InputObject $arrCollection ` -DatabaseName "MyDataBase" -DatabaseTable "[dbo].[MyTable]" -SqlDelimiter '~'; .EXAMPLE Convert-SnsObjectToSQLInsertQuery -Path 'C:\Test.csv' -Delimiter '~' -Encoding 'Unicode' ` -Computer 'computer.contoso.com' -DatabaseName "MyDataBase" -DatabaseTable "[dbo].[MyTable]" ` -UserName "JohnSmith" -Password "Pa$$w0rd"; .LINK svesavov / SnsPsModule - https://github.com/svesavov/SnsPsModule .LINK Svetoslav Savov on LinkedIn - https://www.linkedin.com/in/svetoslavsavov #> [CmdletBinding(PositionalBinding = $false, DefaultParameterSetName = "InputObject")] Param( [Parameter(Mandatory = $true, ParameterSetName = 'InputObject', ` ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $false)] [ValidateCount(1, [System.Int32]::MaxValue)] [ValidateNotNullOrEmpty()][System.Array]$InputObject, [Parameter(Mandatory = $false, ParameterSetName = 'InputObject', ` ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateNotNullOrEmpty()][System.Char]$SqlDelimiter = '~', [Parameter(Mandatory = $true, ParameterSetName = 'FileImport', ` ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateScript({[System.IO.File]::Exists("$($_)")})] [ValidateNotNullOrEmpty()][System.String]$Path, [Parameter(Mandatory = $false, ParameterSetName = 'FileImport', ` ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateNotNullOrEmpty()][System.Char]$Delimiter = ',', [Parameter(Mandatory = $false, ParameterSetName = 'FileImport', ` ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateSet('Unicode', 'UTF7', 'UTF8', 'ASCII', 'UTF32', 'BigEndianUnicode', 'Default', 'OEM')] [ValidateNotNullOrEmpty()][System.String]$Encoding = 'ASCII', [Parameter(Mandatory = $false, ParameterSetName = 'FileImport', ` ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateCount(2, [System.Int32]::MaxValue)] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateNotNullOrEmpty()][System.String[]]$Header, [Parameter(Mandatory = $false, ` ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [Alias('ComputerName','SqlServer','SqlServerName','DbServer','DbServerName','Server','ServerName')] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateScript({("$([Net.DNS]::GetHostEntry(""$(((($_.Split('\'))[0]).Split(','))[0])"").HostName)" ` -like "$(((($_.Split('\'))[0]).Split(','))[0])")})] [ValidateNotNullOrEmpty()][System.String]$Computer, [Parameter(Mandatory = $true, ` ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [Alias('DbName','DataBase','SqlDatabaseName','SqlDbName','SqlDb')] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateNotNullOrEmpty()][System.String]$DatabaseName, [Parameter(Mandatory = $true, ` ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [Alias('TableName','Table','SqlTableName')] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateScript({"$($_)" -match '^\[?\w{1,10}\]?\.\[?\w{1,}\]?$'})] [ValidateNotNullOrEmpty()][System.String]$DatabaseTable, [Parameter(Mandatory = $false, ` ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [Alias('SqlUser',"User","Usr","UID",'DbUser')] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateNotNullOrEmpty()][System.String]$UserName, [Parameter(Mandatory = $false, ` ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [Alias('SqlPassword','DbPassword','pwd','Pass')] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateNotNullOrEmpty()][System.String]$Password, [Parameter(Mandatory = $false, ` ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [Alias('SqlCredentials','SqlCred','Cred')] [ValidateNotNullOrEmpty()][System.Management.Automation.PSCredential]$Credentials, [Parameter(Mandatory = $false, ` ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [Switch]$PassThru ) ##### Override The Begin Method Begin { Write-Debug "Override Begin Method"; Write-Verbose ""; Write-Verbose "Convert-SnsObjectToSQLInsertQuery"; #Write-Verbose "ParameterSetName: $($PSCmdlet.ParameterSetName)`r`n"; ##### Initialize New Measure Watch [System.Diagnostics.Stopwatch]$objCmdStopWatch = [System.Diagnostics.Stopwatch]::StartNew(); If ("$((Get-FileHash -Path ""$($PSCommandPath)"" -Algorithm ""SHA256"" -Verbose:$false -Debug:$false).Hash)" -ne "$($global:SnsModuleCfg.ModuleHash)") { Write-Warning "There Is New Version Of SnsPsModule Module Released. Please Restart The PowerShell Session." -WarningAction "Continue"; }; Write-Verbose "ParameterSetName: $($PSCmdlet.ParameterSetName)`r`n"; [System.String]$strExc = "This CmdLet Is Deprecated. Please Use ""Invoke-SnsMsSqlObjectInsert"" CmdLet From ""https://www.powershellgallery.com/packages/SnsMsSqlPsModule/"" Module."; Write-Error $strExc -ErrorAction "Stop"; ##### Initialize The Variables [System.Collections.Hashtable]$hshSplatting = @{}; [System.String]$strSqlQry = ""; [System.String[]]$arrProperties = @(); [System.String]$strProp = ""; [System.UInt32]$intIndex = 0; [System.String]$arrQryes = @(); [System.Data.DataRow[]]$arrRows = @(); [System.Array]$arrVerif = @(); [System.UInt32]$intInd = 0; [System.Boolean]$bolOut = $true; #================================================================================== #region Import A CSV File #================================================================================== ##### Verify The Parameter Set Write-Debug "Verify The Parameter Set"; If ("$($PSCmdlet.ParameterSetName)" -eq 'FileImport') { ##### Generate The Splatting HashTable Write-Debug "Generate The Splatting HashTable"; [System.Collections.Hashtable]$hshSplatting = @{}; $hshSplatting.Add('Path', "$($Path)"); $hshSplatting.Add('Delimiter', $Delimiter); $hshSplatting.Add('Encoding', "$($Encoding)"); $hshSplatting.Add("Verbose", $false); $hshSplatting.Add("Debug", $false); If ($Header.Count -gt 0) {$hshSplatting.Add('Header', $Header);}; ##### Import The Provided CSV File Write-Debug "Import The Provided CSV File"; [System.Array]$InputObject = @(); [System.Array]$InputObject = Import-Csv @hshSplatting; ##### Verify The CSV File Import Write-Debug "Verify The CSV File Import"; If (($InputObject.Count -le 0) -or (($InputObject[0] | Get-Member -MemberType "*Property" -Verbose:$false -Debug:$false).Count -eq 1)) { Write-Error "Failed To Read ""$($Path)""" -ErrorAction "Continue"; Return; }; Write-Verbose "Imported ""$($Path)""`r`n"; } #================================================================================== #endregion Import A CSV File #================================================================================== #================================================================================== #region Generate The Object Properties / Column Names #================================================================================== ##### Verify Whether The DataBase Server Is Provided Write-Debug "Verify Whether The DataBase Server Is Provided"; If (-not -not "$($Computer)") { ##### Generate Columns Retrieval SQL Query ##### Auto-Increment Columns Will Be Automatically Excluded Write-Debug "Generate Columns Retrieval SQL Query"; [System.String]$strSqlQry = ""; [System.String]$strSqlQry += "SELECT [COLUMN_NAME], [IS_NULLABLE]`r`n"; [System.String]$strSqlQry += "FROM [INFORMATION_SCHEMA].[COLUMNS]`r`n"; [System.String]$strSqlQry += "WHERE`r`n"; [System.String]$strSqlQry += "`t[TABLE_CATALOG] = '$($DatabaseName)'`r`n"; [System.String]$strSqlQry += "`tAND`r`n"; [System.String]$strSqlQry += "`t[TABLE_SCHEMA] = '"; [System.String]$strSqlQry += "$($DatabaseTable.Split('.')[0].Replace('[', '').Replace(']', ''))'`r`n"; [System.String]$strSqlQry += "`tAND`r`n"; [System.String]$strSqlQry += "`t[TABLE_NAME] = '"; [System.String]$strSqlQry += "$($DatabaseTable.Split('.')[1].Replace('[', '').Replace(']', ''))'`r`n"; [System.String]$strSqlQry += "`tAND`r`n"; [System.String]$strSqlQry += "`tNOT [COLUMN_NAME] IN`r`n"; [System.String]$strSqlQry += "`t(`r`n"; [System.String]$strSqlQry += "`t`tSELECT [name]`r`n"; [System.String]$strSqlQry += "`t`tFROM [sys].[identity_columns]`r`n"; [System.String]$strSqlQry += "`t`tWHERE [object_id] = OBJECT_ID('"; [System.String]$strSqlQry += "$($DatabaseTable.Split('.')[1].Replace('[', '').Replace(']', ''))')`r`n"; [System.String]$strSqlQry += "`t);"; ##### Generate The Splatting HashTable Write-Debug "Generate The Splatting HashTable"; [System.Collections.Hashtable]$hshSplatting = @{}; $hshSplatting.Add('Query', "$($strSqlQry)"); $hshSplatting.Add('Computer', "$($Computer)"); $hshSplatting.Add('DatabaseName', "$($DataBaseName)"); $hshSplatting.Add("Verbose", $false); $hshSplatting.Add("Debug", $false); If (-not -not "$($Credentials.UserName)") { ##### Credentials Are Provided Write-Verbose "Connecting To ""$($DataBaseName)"" Using Provided Credential Object`r`n"; $hshSplatting.Add('Credentials', $Credentials); } ElseIf ((-not -not "$($UserName)") -and (-not -not "$($Password)")) { ##### UserName And Password Are Provided Write-Verbose "Connecting To ""$($DataBaseName)"" Using Provided UserName And Password`r`n"; $hshSplatting.Add('UserName', $UserName); $hshSplatting.Add('Password', $Password); } Else { ##### Neither Credential Object Nor UserName And Password Are Provided ##### Using Current User Session Write-Verbose "Connecting To ""$($DataBaseName)"" Using Current User Session`r`n"; $hshSplatting.Add('UseCurrentLogOnSession', $true); } ##### Query The DataBase About The Table Columns Write-Debug "Query The DataBase About The Table Columns"; [System.Array]$arrProperties = @(); [System.Array]$arrProperties = Invoke-SnsSQLQuery @hshSplatting | ` Select-Object ` -ExpandProperty 'Tables' ` -Verbose:$false -Debug:$false ` -ErrorAction "SilentlyContinue" ` -WarningAction "SilentlyContinue" ` -First 1 | ` Select-Object ` -ExpandProperty 'Rows' ` -Verbose:$false -Debug:$false ` -ErrorAction "SilentlyContinue" ` -WarningAction "SilentlyContinue" | ` Select-Object -Property ` @( @{'n' = "Name"; 'e' = {"$($_.'COLUMN_NAME')"}}; @{'n' = 'Mandatory'; 'e' = {"$($_.'IS_NULLABLE')" -eq 'NO'}}; ) ` -Verbose:$false -Debug:$false; ##### ##### Verify The Columns Retrieval Write-Debug "Verify The Columns Retrieval"; If ($arrProperties.Count -le 0) { Write-Error "Failed To Get Column Names From ""$($DatabaseTable)""" -ErrorAction "Continue"; Return; } Else { ##### Process Each Not Nullable Column Write-Debug "Process Each Not Nullable Column"; [System.String]$strProp = ""; ForEach ($strProp in [System.String[]]@(($arrProperties | Where-Object {$_.Mandatory} -Verbose:$false -Debug:$false).Name)) { ##### Verify Whether The Current Not Nullable Column Have Value Write-Debug "Verify Whether The Current Not Nullable Column Have Value"; If (-not -not ($InputObject."$($strProp)" | Where-Object {-not "$($_)"} -Verbose:$false -Debug:$false)) { Write-Error "Property ""$($strProp)"" Does Not Match The Criteria" -ErrorAction "Continue"; Return; }; } ##### Convert The Properties Array Write-Debug "Convert The Properties Array"; [System.String[]]$arrProperties = $arrProperties | Select-Object -ExpandProperty "Name" -Verbose:$false -Debug:$false; Write-Verbose "Column Names Which Will Be Used For Object To Query Conversion:"; $arrProperties | ForEach {Write-Verbose "$($_)"}; Write-Verbose ""; } } Else { ##### Retrieve The Properties Of The Input Object Write-Verbose "Using InputObject Properties Without Verification`r`n"; [System.String[]]$arrProperties = @(); [System.String[]]$arrProperties = $InputObject[0] | ` Get-Member -MemberType "*Property" ` -Verbose:$false -Debug:$false ` -ErrorAction "SilentlyContinue" ` -WarningAction "SilentlyContinue" | ` Select-Object -ExpandProperty "Name" ` -Verbose:$false -Debug:$false ` -ErrorAction "SilentlyContinue" ` -WarningAction "SilentlyContinue"; ##### } ##### Verify The Properties Collection Generation If ($arrProperties.Count -le 0) { Write-Error 'Failed To Generate The Object Properties' -ErrorAction "Continue"; Return; }; #================================================================================== #endregion Generate The Object Properties / Column Names #================================================================================== } ##### Override The Process Method Process { Write-Debug "Override Process Method"; Write-Verbose ""; ##### Convert Each Object To SQL Query Write-Debug "Convert Each Object To SQL Query"; [System.UInt32]$intIndex = 0; For ([System.UInt32]$intIndex = 0; $intIndex -lt $InputObject.Count; $intIndex++) { If ($InputObject.Count -gt 5) { Write-Progress -Activity 'Convert-SnsObjectToSQLInsertQuery' -Id 1 ` -PercentComplete (($intIndex / $InputObject.Count) * 100) ` -Verbose:$false -Debug:$false; ##### } #================================================================================== #region Convert The Current Object To SQL Query #================================================================================== ##### Convert The Current Object To SQL Query Write-Debug "Convert The Current Object To SQL Query"; [System.String]$strSqlQry = ""; [System.String]$strSqlQry += "INSERT INTO [$($DatabaseName)].$($DatabaseTable)`r`n"; [System.String]$strSqlQry += "(`r`n"; ##### Process All Property Names Write-Debug "Process All Property Names"; [System.String]$strProp = ""; ForEach ($strProp in $arrProperties) {[System.String]$strSqlQry += "`t[$($strProp)],`r`n";}; [System.String]$strSqlQry = "$($strSqlQry.TrimEnd(""`r`n"").TrimEnd(','))`r`n"; [System.String]$strSqlQry += ")`r`n"; ##### Process All Property Values Write-Debug "Process All Property Values"; [System.String]$strSqlQry += "VALUES`r`n"; [System.String]$strSqlQry += "(`r`n"; [System.String]$strProp = ""; ForEach ($strProp in $arrProperties) { ##### Verify Whether The Current Property Have Value If ( ` (-not -not $InputObject[$intIndex]."$($strProp)") ` -and ` ("$($InputObject[$intIndex].""$($strProp)"")" -ne 'NULL') ` ) { ##### There Is A Value Add It To SQL Query [System.String]$strSqlQry += "`t'$(""$( ` [System.String]::Join($SqlDelimiter, $InputObject[$intIndex].""$($strProp)"") ` )"".Replace(""'"", ""''""))',`r`n"; ##### } Else { ##### There Is No Value Add NULL As Value (It Is Mandatory) [System.String]$strSqlQry += "`tNULL,`r`n"; } } [System.String]$strSqlQry = "$($strSqlQry.TrimEnd(""`r`n"").TrimEnd(','))`r`n"; [System.String]$strSqlQry += ");`r`n"; #================================================================================== #endregion Convert The Current Object To SQL Query #================================================================================== #================================================================================== #region Process The Generated Query #================================================================================== ##### Verify Whether The SQL Server Is Provided Write-Debug "Verify Whether The SQL Server Is Provided"; If (-not -not "$($Computer)") { ##### Add The Generated SQL Query To The Collection For Inserting [System.String]$arrQryes += $strSqlQry; } Else { ##### Pass The Output Object To The Pipeline Write-Debug "Pass Output Object To The Pipeline"; $PSCmdlet.WriteObject($strSqlQry); } Write-Verbose "$($strSqlQry)`r`n"; #================================================================================== #endregion Process The Generated Query #================================================================================== } ##### Close The Progress Bar Write-Debug "Close The Progress Bar"; If ($InputObject.Count -gt 5) { Write-Progress -Activity 'Convert-SnsObjectToSQLInsertQuery' -Id 1 -PercentComplete 100 -Verbose:$false -Debug:$false; Write-Progress -Activity 'Convert-SnsObjectToSQLInsertQuery' -Id 1 -Completed -Verbose:$false -Debug:$false; } ##### Check Whether Queries Are Generated And SQL Server Is Provided Write-Debug "Check Whether Queries Are Generated And SQL Server Is Provided"; If (($arrQryes.Count -gt 0) -and (-not -not "$($Computer)")) { #================================================================================== #region Insert The Generated SQL Queries Into The SQL DataBase #================================================================================== ##### Generate The Splatting HashTable Write-Debug "Generate The Splatting HashTable"; [System.Collections.Hashtable]$hshSplatting = @{}; $hshSplatting.Add('Query', $arrQryes); $hshSplatting.Add('Computer', "$($Computer)"); $hshSplatting.Add('DatabaseName', "$($DataBaseName)"); $hshSplatting.Add("Verbose", $false); $hshSplatting.Add("Debug", $false); If (-not -not "$($Credentials.UserName)") { ##### Credentials Are Provided Write-Verbose "Connecting To ""$($DataBaseName)"" Using Provided Credential Object`r`n"; $hshSplatting.Add('Credentials', $Credentials); } ElseIf ((-not -not "$($UserName)") -and (-not -not "$($Password)")) { ##### UserName And Password Are Provided Write-Verbose "Connecting To ""$($DataBaseName)"" Using Provided UserName And Password`r`n"; $hshSplatting.Add('UserName', $UserName); $hshSplatting.Add('Password', $Password); } Else { ##### Neither Credential Object Nor UserName And Password Are Provided ##### Using Current User Session Write-Verbose "Connecting To ""$($DataBaseName)"" Using Current User Session`r`n"; $hshSplatting.Add('UseCurrentLogOnSession', $true); } ##### Insert Into The DataBase Table The Generated Queries Write-Debug "Insert Into The DataBase Table The Generated Queries"; Invoke-SnsSQLQuery @hshSplatting | Out-Null; #================================================================================== #endregion Insert The Generated SQL Queries Into The SQL DataBase #================================================================================== #================================================================================== #region Verify The Insert #================================================================================== ##### Check Whether Verification Is Requested If ($PassThru.IsPresent) { ##### Generate The InputObject Properties Write-Debug "Generate The InputObject Properties"; [System.String[]]$arrProperties = @(); [System.String[]]$arrProperties = $InputObject[0] | ` Get-Member -MemberType "*Property" ` -Verbose:$false -Debug:$false ` -ErrorAction "SilentlyContinue" ` -WarningAction "SilentlyContinue" | ` Select-Object -ExpandProperty "Name" ` -Verbose:$false -Debug:$false ` -ErrorAction "SilentlyContinue" ` -WarningAction "SilentlyContinue"; ##### ##### Generate The Splatting HashTable Write-Debug "Generate The Splatting HashTable"; [System.Collections.Hashtable]$hshSplatting = @{}; $hshSplatting.Add('Query', "SELECT * FROM [$($DatabaseName)].$($DatabaseTable);"); $hshSplatting.Add('Computer', "$($Computer)"); $hshSplatting.Add('DatabaseName', "$($DataBaseName)"); $hshSplatting.Add("Verbose", $false); $hshSplatting.Add("Debug", $false); If (-not -not "$($Credentials.UserName)") { ##### Credentials Are Provided Write-Debug "Connecting To ""$($DataBaseName)"" Using Provided Credential Object`r`n"; $hshSplatting.Add('Credentials', $Credentials); } ElseIf ((-not -not "$($UserName)") -and (-not -not "$($Password)")) { ##### UserName And Password Are Provided Write-Debug "Connecting To ""$($DataBaseName)"" Using Provided UserName And Password`r`n"; $hshSplatting.Add('UserName', $UserName); $hshSplatting.Add('Password', $Password); } Else { ##### Neither Credential Object Nor UserName And Password Are Provided ##### Using Current User Session Write-Debug "Connecting To ""$($DataBaseName)"" Using Current User Session`r`n"; $hshSplatting.Add('UseCurrentLogOnSession', $true); } ##### Retrieve The Data From The SQL Table Write-Verbose "Query The SQL Table About The Inserted Data"; [System.Data.DataRow[]]$arrRows = @(); [System.Data.DataRow[]]$arrRows = Invoke-SnsSQLQuery @hshSplatting | ` Select-Object -ExpandProperty 'Tables' ` -Verbose:$false -Debug:$false ` -ErrorAction "SilentlyContinue" ` -WarningAction "SilentlyContinue" | ` Select-Object -ExpandProperty 'Rows' ` -Verbose:$false -Debug:$false ` -ErrorAction "SilentlyContinue" ` -WarningAction "SilentlyContinue"; ##### ##### Verify Each Input Object Write-Debug "Verify Each Input Object"; [System.UInt32]$intIndex = 0; For ([System.UInt32]$intIndex = 0; $intIndex -lt $InputObject.Count; $intIndex++) { If ($InputObject.Count -gt 5) { Write-Progress -Activity 'Verify-SnsObjectToSQLInsertQuery' -Id 1 ` -PercentComplete (($intIndex / $InputObject.Count) * 100) ` -Verbose:$false -Debug:$false; ##### } ##### Assign The SQL Data To A Temp Collection Write-Debug "Assign The SQL Data To A Temp Collection"; [System.Array]$arrVerif = @(); [System.Array]$arrVerif = $arrRows | Select-Object -Property "*" -Verbose:$false -Debug:$false; ##### Verify Each Property Of The Current Object Write-Debug "Verify Each Property Of The Current Object"; [System.UInt32]$intInd = 0; For ([System.UInt32]$intInd = 0; $intInd -lt $arrProperties.Count; $intInd++) { ##### Filter The Verification Collection To Objects That Have The Same Value ##### In The Same Property As The Input Object Write-Debug "Filter The Verification Collection To Objects That Match The Current Property Value"; [System.Array]$arrVerif = $arrVerif | Where-Object ` { ` "$($_.""$($arrProperties[$intInd])"")" ` -eq ` "$($InputObject[$intIndex].""$($arrProperties[$intInd])"")" ` } ` -Verbose:$false -Debug:$false; } ##### Verify Whether The Verification Collection Contains Objects ##### That Have All The Property Values Matching With The InputObject Write-Debug "Verify Whether The Verification Collection Contains Objects"; If ($arrVerif.Count -le 0) { Write-Verbose "Input Object ""$($intIndex)"" Was Not Found In The SQL Table"; [System.Boolean]$bolOut = $false; [System.UInt32]$intIndex = $InputObject.Count + 1; } } ##### Close The Progress Bar Write-Debug "Close The Progress Bar"; If ($InputObject.Count -gt 5) { Write-Progress -Activity 'Verify-SnsObjectToSQLInsertQuery' -Id 1 -PercentComplete 100 -Verbose:$false -Debug:$false; Write-Progress -Activity 'Verify-SnsObjectToSQLInsertQuery' -Id 1 -Completed -Verbose:$false -Debug:$false; } ##### Pass The Output Object To The Pipeline Write-Debug "Pass Output Object To The Pipeline"; $PSCmdlet.WriteObject($bolOut); } #================================================================================== #endregion Verify The Insert #================================================================================== } } ##### Override The End Method End { Write-Debug "Override End Method"; Write-Verbose ""; ##### Reset The Variables Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "bolOut"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "intInd"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "arrVerif"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "arrRows"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "intIndex"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strProp"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "arrProperties"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strSqlQry"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "hshSplatting"; $PSCmdlet.MyInvocation.BoundParameters.Keys | ForEach { Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "$($_)"; } ##### Stop The StopWatch $objCmdStopWatch.Stop(); Write-Verbose "Command Elapsed: ""$($objCmdStopWatch.ElapsedMilliseconds)"" Milliseconds." ; Write-Verbose "End!"; } } ##### Disable-SnsMfa ============================================================== Function Disable-SnsMfa () { <# .SYNOPSIS Disables Per User MultiFactor Authentication For A Specified Azure Account. .DESCRIPTION Disables Per User MultiFactor Authentication For A Specified Azure Account. The CmdLet Disables The Per User MFA Gracefully, In Terms That Any Set By The User StrongAuthenticationMethods Are Preserved. Which Means That If The MFA Is Disabled Temporary After Re-Enablement The User Wont Be Required To Set Up The MFA From Scratch. Which Means That The Authentication App And The Remaining Configuration Is Preserved. Unfortunately The AppPasswords Cannot Be Preserved. They Can Be Used Only Whenever MFA Is Enabled And Are Lost Immediately With The Disablement. The CmdLet Accept As Input The AzureAD Account UserPrincipalName String, AzureAD Account ObjectId String Or AzureAD MSOL User Object. On Input Are Evaluated The TypeName Of The Provided Objects. Therefore The CmdLet Will Accept Input From Pipeline Or Collection Variable Of All The Specified TypeName Simultaneously. Using WhatIf Switch Parameter Allows The CmdLet To Be Used For MFA Report Generation Without Actually Modification Of Users MFA Status. .PARAMETER InputObject Specifies Either MsolUser Object, Or UserPrincipalName, Or AzureAD ObjectId Of The User Or Users Which Have To Be MFA Enabled. Parameter Alias: "UserPrincipalName", "ObjectId" Parameter Validation: Yes Using Object TypeName And RegEx Matching Validation .PARAMETER Attempts Specifies The Number Of Attempts That Have To Be Made To Enable The MFA. Parameter Alias: N/A Parameter Validation: N/A .PARAMETER WhatIf Specifies To The CmdLet To Show All The Actions That Will Normally Do Without Doing Them. Lack Of Errors During WhatIf Execution Is Not Indicator That There Will Be No Any During Real Execution. Parameter Alias: N/A Parameter Validation: N/A .INPUTS Pipeline Input -InputObject Which Accept Values Of Type [System.Array] And [System.String[]] .OUTPUTS Pipeline Output [SnsPsModule.SnsMfaStatus[]] Which Contains A Report About Users MFA Status .NOTES AUTHOR: Svetoslav Nedyalkov Savov THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER. .EXAMPLE [SnsPsModule.SnsMfaStatus[]]$arrMfa = Disable-SnsMfa -InputObject $arrCollection; .LINK svesavov / SnsPsModule - https://github.com/svesavov/SnsPsModule .LINK Svetoslav Savov on LinkedIn - https://www.linkedin.com/in/svetoslavsavov #> [CmdletBinding(PositionalBinding = $false, ` SupportsShouldProcess = $true, ConfirmImpact = "High")] Param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] [Alias("UserPrincipalName", "ObjectId")] [ValidateNotNullOrEmpty()] [ValidateScript({( ` ("$(($_ | Get-Member -Verbose:$false -Debug:$false )[0].TypeName)" -like "*Microsoft.Online.Administration.User") ` -or ` ("$($_)" -match "$([SnsPsModule.SnsPatterns]::UPNPattern)") ` -or ` ("$($_)" -match "$([SnsPsModule.SnsPatterns]::GUIDPattern)") ` )})] [System.Array]$InputObject, [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateNotNullOrEmpty()][System.Int32]$Attempts = 3 ) ##### Override The Begin Method Begin { Write-Debug "Override Begin Method"; Write-Verbose ""; Write-Verbose "Disable-SnsMfa"; #Write-Verbose "ParameterSetName: $($PSCmdlet.ParameterSetName)`r`n"; ##### Initialize New Measure Watch [System.Diagnostics.Stopwatch]$objCmdStopWatch = [System.Diagnostics.Stopwatch]::StartNew(); If ("$((Get-FileHash -Path ""$($PSCommandPath)"" -Algorithm ""SHA256"" -Verbose:$false -Debug:$false).Hash)" -ne "$($global:SnsModuleCfg.ModuleHash)") { Write-Warning "There Is New Version Of SnsPsModule Module Released. Please Restart The PowerShell Session." -WarningAction "Continue"; }; #================================================================================== #region Verify The Msol V1 Service Connection #================================================================================== ##### Verify The Msol V1 Service Connection Write-Verbose "Verify The Msol V1 Service Connection"; If (-not (Get-Variable -Verbose:$false -Debug:$false | Where-Object {"$($_.Name)" -like "ArrMsolLicenses"} -Verbose:$false -Debug:$false)) { Write-Error "Please Establish A Connection To Msol Service" -ErrorAction "Stop"; } #================================================================================== #endregion Verify The Msol V1 Service Connection #================================================================================== ##### Initialize The Variables [System.Boolean]$bolInteractive = [System.Environment]::UserInteractive; [System.Boolean]$bolProgrBar = $false; [System.UInt32]$intI = 0; [SnsPsModule.SnsMfaStatus]$objMfa = $null; [Microsoft.Online.Administration.User[]]$arrMsolUsr = @(); [Microsoft.Online.Administration.StrongAuthenticationMethod[]]$arrAuthMethods = $null; [System.Int32]$intIn = 0; [System.Boolean]$bolMethOk = $false; } ##### Override The Process Method Process { Write-Debug "Override Process Method"; Write-Verbose ""; ##### Verify Whether There Are InputObjects Write-Debug "Verify Whether There Are InputObjects"; If ($InputObject.Count -gt 0) { ##### Evaluate Whether ProgressBar Is Required [System.Boolean]$bolProgrBar = $bolInteractive -and ($InputObject.Count -gt 5); ##### Process Each Input Object Write-Debug "Process Each Input Object"; [System.UInt32]$intI = 0; For ([System.UInt32]$intI = 0; $intI -lt $InputObject.Count; $intI++) { ##### Evaluate The Number Of Input Objects If ($bolProgrBar) { Write-Progress -Activity "Disable-SnsMfa" -Id 1 -PercentComplete (($intI * 100) / $InputObject.Count) -Verbose:$false -Debug:$false; } #================================================================================== #region Retrieve MsolUser Object From AzureAD #================================================================================== ##### Generate A Mfa Object Write-Debug "Generate A Mfa Object"; [SnsPsModule.SnsMfaStatus]$objMfa = [SnsPsModule.SnsMfaStatus]::new(); ##### Verify What Kind Of Input Was Provided Write-Debug "Verify What Kind Of Input Was Provided"; If ("$(($InputObject[$intI] | Get-Member -Verbose:$false -Debug:$false )[0].TypeName)" -like "*Microsoft.Online.Administration.User") { ##### Assign The Msol User Object To The Corresponding Object Property $objMfa.MsolUser = $InputObject[$intI] | Select-Object -Property * -Verbose:$false -Debug:$false; $objMfa.MsolUser | Add-Member -TypeName "Selected.Microsoft.Online.Administration.User"; ##### Verify The Provided Object Write-Debug "Verify The Provided Object"; If ( ` ("$($InputObject[$intI].ObjectId.Guid)" -notmatch "$([SnsPsModule.SnsPatterns]::GUIDPattern)") ` -or ` ("$($InputObject[$intI].UserPrincipalName)" -notmatch "$([SnsPsModule.SnsPatterns]::UPNPattern)") ` ) { Write-Error "Unable To Recognize The Provided Input Object"; $PSCmdlet.WriteObject($objMfa); Continue; } } ElseIf ("$($InputObject[$intI])" -match "$([SnsPsModule.SnsPatterns]::UPNPattern)") { ##### Assign The UPN Property To The Corresponding Object Property $objMfa.UserPrincipalName = "$($InputObject[$intI])"; ##### Query The Msol V1 About User With UPN Write-Debug "Query The Msol V1 About User With UPN: ""$($objMfa.UserPrincipalName)"""; [Microsoft.Online.Administration.User[]]$arrMsolUsr = @(); [Microsoft.Online.Administration.User[]]$arrMsolUsr = Get-MsolUser -UserPrincipalName "$($objMfa.UserPrincipalName)" -Verbose:$false -Debug:$false; If ($arrMsolUsr.Count -eq 1) { $objMfa.MsolUser = $arrMsolUsr[0] | Select-Object -Property * -Verbose:$false -Debug:$false; } ElseIf ($arrMsolUsr.Count -lt 1) { Write-Error "Unable To Find MsolUser With UPN: ""$($objMfa.UserPrincipalName)"""; $PSCmdlet.WriteObject($objMfa); Continue; } ElseIf ($arrMsolUsr.Count -gt 1) { Write-Error "UPN Conflict in Msol V1 Service About ""$($objMfa.UserPrincipalName)"""; $PSCmdlet.WriteObject($objMfa); Continue; } } ElseIf ("$($InputObject[$intI])" -match "$([SnsPsModule.SnsPatterns]::GUIDPattern)") { ##### Assign The UPN Property To The Corresponding Object Property $objMfa.ObjectId = "$($InputObject[$intI])"; ##### Query The Msol V1 About User With UPN Write-Debug "Query The Msol V1 About User With ObjectId: ""$($objMfa.ObjectId)"""; [Microsoft.Online.Administration.User[]]$arrMsolUsr = @(); [Microsoft.Online.Administration.User[]]$arrMsolUsr = Get-MsolUser -ObjectId "$($objMfa.ObjectId)" -Verbose:$false -Debug:$false; If ($arrMsolUsr.Count -eq 1) { $objMfa.MsolUser = $arrMsolUsr[0] | Select-Object -Property * -Verbose:$false -Debug:$false; } ElseIf ($arrMsolUsr.Count -lt 1) { Write-Error "Unable To Find MsolUser With ObjectId: ""$($objMfa.ObjectId)"""; $PSCmdlet.WriteObject($objMfa); Continue; } ElseIf ($arrMsolUsr.Count -gt 1) { Write-Error "ObjectId Conflict in Msol V1 Service About ""$($objMfa.ObjectId)"""; $PSCmdlet.WriteObject($objMfa); Continue; } } #================================================================================== #endregion Retrieve MsolUser Object From AzureAD #================================================================================== #================================================================================== #region Calculate The Initial State #================================================================================== ##### Continue If The MsolUser Is Generated Write-Debug "Continue If The MsolUser Is Generated"; If ("$(($objMfa.MsolUser | Get-Member -Verbose:$false -Debug:$false)[0].TypeName)" -eq "Selected.Microsoft.Online.Administration.User") { ##### Evaluate The Current Object State Write-Debug "Evaluate The Current Object State"; $objMfa.UserPrincipalName = "$($objMfa.MsolUser.UserPrincipalName)"; $objMfa.DisplayName = "$($objMfa.MsolUser.DisplayName)"; $objMfa.ObjectId = "$($objMfa.MsolUser.ObjectId.Guid)"; $objMfa.MfaState = "$($objMfa.MsolUser.StrongAuthenticationRequirements[0].State)"; $objMfa.ValueCorrect = (-not "$($objMfa.MfaState)"); Write-Verbose "Found UPN: ""$($objMfa.UserPrincipalName)"" ObjectId: ""$($objMfa.ObjectId)"" MfaState: ""$($objMfa.MfaState)"""; ##### Get Users StrongAuthenticationMethods [Microsoft.Online.Administration.StrongAuthenticationMethod[]]$arrAuthMethods = $objMfa.MsolUser.StrongAuthenticationMethods; } #================================================================================== #endregion Calculate The Initial State #================================================================================== #================================================================================== #region Disable-SnsMfa #================================================================================== ##### Continue If The MsolUser Is Retrieved Successfully Write-Debug "Continue If The MsolUser Is Retrieved Successfully" If ("$(($objMfa.MsolUser | Get-Member -Verbose:$false -Debug:$false)[0].TypeName)" -eq "Selected.Microsoft.Online.Administration.User") { ##### Verify Whether The MFA Is Not Enabled Write-Debug "Verify Whether The MFA Is Not Enabled"; If (-not $objMfa.ValueCorrect) { ##### Invoke ShouldProcess Method Write-Debug "Invoke ShouldProcess Method"; If ($PSCmdlet.ShouldProcess("$($objMfa.UserPrincipalName)")) { #================================================================================== #region Disable-MFA #================================================================================== ##### Loop The MFA Disablement Write-Debug "Loop The MFA Disablement"; [System.Int32]$intIn = 0; While ((-not $objMfa.ValueCorrect) -and ($intIn -lt $Attempts)) { ##### Disable MFA Of The User Write-Verbose "Disable MFA Of: ""$($objMfa.UserPrincipalName)"""; Set-MsolUser -ObjectId "$($objMfa.ObjectId)" -StrongAuthenticationRequirements @() -Verbose:$false -Debug:$false | Out-Null; ##### Process The Loop Variables Write-Debug "Process The Loop Variables"; [System.Int32]$intIn = $intIn + 1; $objMfa.ValueModified = $true; $objMfa.MsolUser = $null; $objMfa.MfaState = "Enabled"; $objMfa.ValueCorrect = $false; Start-Sleep -Seconds 1 -Verbose:$false -Debug:$false; ##### Query The Msol V1 About User With UPN Write-Debug "Query The Msol V1 About User With UPN: ""$($objMfa.UserPrincipalName)"""; [Microsoft.Online.Administration.User[]]$arrMsolUsr = @(); [Microsoft.Online.Administration.User[]]$arrMsolUsr = Get-MsolUser -ObjectId "$($objMfa.ObjectId)" -Verbose:$false -Debug:$false; ##### Verify The Msol V1 Query Output Write-Debug "Verify The Msol V1 Query Output"; If ($arrMsolUsr.Count -eq 1) { ##### Update The Object Properties Write-Debug "Update The Object Properties"; $objMfa.MsolUser = $arrMsolUsr[0] | Select-Object -Property * -Verbose:$false -Debug:$false; $objMfa.MfaState = "$($objMfa.MsolUser.StrongAuthenticationRequirements[0].State)"; $objMfa.ValueCorrect = (-not "$($objMfa.MfaState)"); } } #================================================================================== #endregion Disable-MFA #================================================================================== #================================================================================== #region Put The StrongAuthenticationMethods Back #================================================================================== ##### Check Whether There Are Methods To Be Reverted Write-Debug "Check Whether There Are Methods To Be Reverted"; If ($arrAuthMethods.Count -gt 0) { ##### Verify The Current Authentication Methods [System.Boolean]$bolMethOk = $false; [System.Boolean]$bolMethOk = ($arrAuthMethods.Count -eq $objMfa.MsolUser.StrongAuthenticationMethods.Count); ##### Loop The MFA Methods Insert Write-Debug "Loop The MFA Methods Insert"; [System.Int32]$intIn = 0; While ((-not $bolMethOk) -and ($intIn -lt $Attempts)) { ##### Set StrongAuthenticationMethods Back To The User Write-Verbose "Set The Old StrongAuthenticationMethods Back To: ""$($objMfa.UserPrincipalName)"""; Set-MsolUser -ObjectId "$($objMfa.ObjectId)" -StrongAuthenticationMethods $arrAuthMethods -Verbose:$false -Debug:$false | Out-Null; ##### Process The Loop Variables Write-Debug "Process The Loop Variables"; [System.Int32]$intIn = $intIn + 1; $objMfa.MsolUser = $null; [System.Boolean]$bolMethOk = $false; Start-Sleep -Seconds 1 -Verbose:$false -Debug:$false; ##### Query The Msol V1 About User With UPN Write-Debug "Query The Msol V1 About User With UPN: ""$($objMfa.UserPrincipalName)"""; [Microsoft.Online.Administration.User[]]$arrMsolUsr = @(); [Microsoft.Online.Administration.User[]]$arrMsolUsr = Get-MsolUser -ObjectId "$($objMfa.ObjectId)" -Verbose:$false -Debug:$false; ##### ##### Verify The Msol V1 Query Output Write-Debug "Verify The Msol V1 Query Output"; If ($arrMsolUsr.Count -eq 1) { ##### Update The Object Properties Write-Debug "Update The Object Properties"; $objMfa.MsolUser = $arrMsolUsr[0] | Select-Object -Property * -Verbose:$false -Debug:$false; [System.Boolean]$bolMethOk = ($arrAuthMethods.Count -eq $objMfa.MsolUser.StrongAuthenticationMethods.Count); } } ##### Verify Whether The Methods Are Set Back or The Number Of Attempts Exceeded If (-not $bolMethOk) { $objMfa.ValueCorrect = $false; }; } #================================================================================== #endregion Put The StrongAuthenticationMethods Back #================================================================================== } } Else { Write-Verbose "The MFA Of User: ""$($objMfa.UserPrincipalName)"" Is Already Disabled"; } } #================================================================================== #endregion Disable-SnsMfa #================================================================================== ##### Pass The Output Object To The Pipeline Write-Debug "Pass Output Object To The Pipeline"; $PSCmdlet.WriteObject($objMfa); } If ($bolProgrBar) { Write-Progress -Activity "Disable-SnsMfa" -Id 1 -PercentComplete 100 -Verbose:$false -Debug:$false; Write-Progress -Activity "Disable-SnsMfa" -Id 1 -Completed -Verbose:$false -Debug:$false; } } } ##### Override The End Method End { Write-Debug "Override End Method"; Write-Verbose ""; ##### Reset The Variables Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "bolMethOk"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "intIn"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "arrAuthMethods"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "arrMsolUsr"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "objMfa"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "intI"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "bolProgrBar"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "bolInteractive"; $PSCmdlet.MyInvocation.BoundParameters.Keys | ForEach { Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "$($_)"; } ##### Stop The StopWatch $objCmdStopWatch.Stop(); Write-Verbose "Command Elapsed: ""$($objCmdStopWatch.ElapsedMilliseconds)"" Milliseconds." ; Write-Verbose "End!"; } } ##### Enable-SnsMfa =============================================================== Function Enable-SnsMfa () { <# .SYNOPSIS Enables Per User MultiFactor Authentication For A Specified Azure Account. .DESCRIPTION Enables Per User MultiFactor Authentication For A Specified Azure Account. The CmdLet Accepts As Input The AzureAD Account UserPrincipalName String, AzureAD Account ObjectId String Or AzureAD MSOL User Object. On Input Are Evaluated The TypeName Of The Provided Objects. Therefore The CmdLet Will Accept Input From Pipeline Or Collection Variable Of All The Specified TypeName Simultaneously. Using WhatIf Switch Parameter Allows The CmdLet To Be Used For MFA Report Generation Without Actually Modification Of Users MFA Status. .PARAMETER InputObject Specifies Either MsolUser Object, Or UserPrincipalName, Or AzureAD ObjectId Of The User Or Users Which Have To Be MFA Enabled. Parameter Alias: "UserPrincipalName", "ObjectId" Parameter Validation: Yes Using Object TypeName And RegEx Matching Validation .PARAMETER AuthRequirement Specifies A Microsoft.Online.Administration.StrongAuthenticationRequirement Object With The Required MFA Parameters. If Omitted The CmdLet Will Generate One Internally With Default Parameters: -- MFA Status "Enabled" -- Keep Any Existing Authentication Methods And Devices. Parameter Alias: N/A Parameter Validation: Yes, Using Object TypeName Validation .PARAMETER Attempts Specifies The Number Of Attempts That Have To Be Made To Enable The MFA. Parameter Alias: N/A Parameter Validation: N/A .PARAMETER Force Specifies To The CmdLet That Exact Matching Of Enforce And Enabled Have To Be Used. If Omitted The CmdLet Will Consider Users With Enabled MFA To Be Compliant When AuthRequirement Object Require Enforced MFA And Vice Versa. Parameter Alias: N/A Parameter Validation: N/A .PARAMETER WhatIf Specifies To The CmdLet To Show All The Actions That Will Normally Do Without Doing Them. Lack Of Errors During WhatIf Execution Is Not Indicator That There Will Be No Any During Real Execution. Parameter Alias: N/A Parameter Validation: N/A .INPUTS Pipeline Input -InputObject Which Accept Values Of Type [System.Array] And [System.String[]] .OUTPUTS Pipeline Output [SnsPsModule.SnsMfaStatus[]] Which Contains A Report About Users MFA Status .NOTES AUTHOR: Svetoslav Nedyalkov Savov THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER. .EXAMPLE [SnsPsModule.SnsMfaStatus[]]$arrMfa = Enable-SnsMfa -InputObject $arrCollection ` -AuthRequirement $objAuthRequirement -Force; .LINK svesavov / SnsPsModule - https://github.com/svesavov/SnsPsModule .LINK Svetoslav Savov on LinkedIn - https://www.linkedin.com/in/svetoslavsavov #> [CmdletBinding(PositionalBinding = $false, ` SupportsShouldProcess = $true, ConfirmImpact = "Low")] Param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] [Alias("UserPrincipalName", "ObjectId")] [ValidateNotNullOrEmpty()] [ValidateScript({( ` ("$(($_ | Get-Member -Verbose:$false -Debug:$false )[0].TypeName)" -like "*Microsoft.Online.Administration.User") ` -or ` ("$($_)" -match "$([SnsPsModule.SnsPatterns]::UPNPattern)") ` -or ` ("$($_)" -match "$([SnsPsModule.SnsPatterns]::GUIDPattern)") ` )})] [System.Array]$InputObject, [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateNotNullOrEmpty()] [ValidateScript({"$(($_ | Get-Member -Verbose:$false -Debug:$false )[0].TypeName)" -eq "Microsoft.Online.Administration.StrongAuthenticationRequirement"})] [System.Object]$AuthRequirement, [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateNotNullOrEmpty()][System.Int32]$Attempts = 3, [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [Switch]$Force = $false ) ##### Override The Begin Method Begin { Write-Debug "Override Begin Method"; Write-Verbose ""; Write-Verbose "Enable-SnsMfa"; #Write-Verbose "ParameterSetName: $($PSCmdlet.ParameterSetName)`r`n"; ##### Initialize New Measure Watch [System.Diagnostics.Stopwatch]$objCmdStopWatch = [System.Diagnostics.Stopwatch]::StartNew(); If ("$((Get-FileHash -Path ""$($PSCommandPath)"" -Algorithm ""SHA256"" -Verbose:$false -Debug:$false).Hash)" -ne "$($global:SnsModuleCfg.ModuleHash)") { Write-Warning "There Is New Version Of SnsPsModule Module Released. Please Restart The PowerShell Session." -WarningAction "Continue"; }; #================================================================================== #region Verify The Msol V1 Service Connection #================================================================================== ##### Verify The Msol V1 Service Connection Write-Verbose "Verify The Msol V1 Service Connection"; If (-not (Get-Variable -Verbose:$false -Debug:$false | Where-Object {"$($_.Name)" -like "ArrMsolLicenses"} -Verbose:$false -Debug:$false)) { Write-Error "Please Establish A Connection To Msol Service" -ErrorAction "Stop"; } #================================================================================== #endregion Verify The Msol V1 Service Connection #================================================================================== ##### Initialize The Variables [Microsoft.Online.Administration.StrongAuthenticationRequirement]$objAuthRequirement = $null; [System.Boolean]$bolInteractive = [System.Environment]::UserInteractive; [System.Boolean]$bolProgrBar = $false; [System.UInt32]$intI = 0; [SnsPsModule.SnsMfaStatus]$objMfa = $null; [Microsoft.Online.Administration.User[]]$arrMsolUsr = @(); [System.Int32]$intIn = 0; #================================================================================== #region Generate The StrongAuthenticationRequirement Object #================================================================================== ##### Verify Whether StrongAuthenticationRequirement Object Is Provided Write-Debug "Verify Whether StrongAuthenticationRequirement Object Is Provided"; If (-not "$($AuthRequirement.State)") { ##### Generate The StrongAuthenticationRequirement Object Write-Verbose "Generate The StrongAuthenticationRequirement Object"; [Microsoft.Online.Administration.StrongAuthenticationRequirement]$objAuthRequirement = $null; [Microsoft.Online.Administration.StrongAuthenticationRequirement]$objAuthRequirement = New-Object -TypeName 'Microsoft.Online.Administration.StrongAuthenticationRequirement'; $objAuthRequirement.RelyingParty = "*"; #$objAuthRequirement.State = 'Enforced'; $objAuthRequirement.State = 'Enabled'; $objAuthRequirement.RememberDevicesNotIssuedBefore = [System.DateTime]::Now; } Else { [Microsoft.Online.Administration.StrongAuthenticationRequirement]$objAuthRequirement = $null; [Microsoft.Online.Administration.StrongAuthenticationRequirement]$objAuthRequirement = $AuthRequirement } #================================================================================== #endregion Generate The StrongAuthenticationRequirement Object #================================================================================== } ##### Override The Process Method Process { Write-Debug "Override Process Method"; Write-Verbose ""; ##### Verify Whether There Are InputObjects Write-Debug "Verify Whether There Are InputObjects"; If ($InputObject.Count -gt 0) { ##### Evaluate Whether ProgressBar Is Required [System.Boolean]$bolProgrBar = $bolInteractive -and ($InputObject.Count -gt 5); ##### Process Each Input Object Write-Debug "Process Each Input Object"; [System.UInt32]$intI = 0; For ([System.UInt32]$intI = 0; $intI -lt $InputObject.Count; $intI++) { If ($bolProgrBar) { Write-Progress -Activity "Enable-SnsMfa" -Id 1 -PercentComplete (($intI * 100) / $InputObject.Count) -Verbose:$false -Debug:$false; } #================================================================================== #region Retrieve MsolUser Object From AzureAD #================================================================================== ##### Generate A Mfa Object Write-Debug "Generate A Mfa Object"; [SnsPsModule.SnsMfaStatus]$objMfa = [SnsPsModule.SnsMfaStatus]::new(); ##### Verify What Kind Of Input Was Provided Write-Debug "Verify What Kind Of Input Was Provided"; If ("$(($InputObject[$intI] | Get-Member -Verbose:$false -Debug:$false )[0].TypeName)" -like "*Microsoft.Online.Administration.User") { ##### Assign The Msol User Object To The Corresponding Object Property $objMfa.MsolUser = $InputObject[$intI] | Select-Object -Property * -Verbose:$false -Debug:$false; $objMfa.MsolUser | Add-Member -TypeName "Selected.Microsoft.Online.Administration.User"; ##### Verify The Provided Object Write-Debug "Verify The Provided Object"; If ( ` ("$($InputObject[$intI].ObjectId.Guid)" -notmatch "$([SnsPsModule.SnsPatterns]::GUIDPattern)") ` -or ` ("$($InputObject[$intI].UserPrincipalName)" -notmatch "$([SnsPsModule.SnsPatterns]::UPNPattern)") ` ) { Write-Error "Unable To Recognize The Provided Input Object"; $PSCmdlet.WriteObject($objMfa); Continue; } } ElseIf ("$($InputObject[$intI])" -match "$([SnsPsModule.SnsPatterns]::UPNPattern)") { ##### Assign The UPN Property To The Corresponding Object Property $objMfa.UserPrincipalName = "$($InputObject[$intI])"; ##### Query The Msol V1 About User With UPN Write-Debug "Query The Msol V1 About User With UPN: ""$($objMfa.UserPrincipalName)"""; [Microsoft.Online.Administration.User[]]$arrMsolUsr = @(); [Microsoft.Online.Administration.User[]]$arrMsolUsr = Get-MsolUser -UserPrincipalName "$($objMfa.UserPrincipalName)" -Verbose:$false -Debug:$false; If ($arrMsolUsr.Count -eq 1) { $objMfa.MsolUser = $arrMsolUsr[0] | Select-Object -Property * -Verbose:$false -Debug:$false; } ElseIf ($arrMsolUsr.Count -lt 1) { Write-Error "Unable To Find MsolUser With UPN: ""$($objMfa.UserPrincipalName)"""; $PSCmdlet.WriteObject($objMfa); Continue; } ElseIf ($arrMsolUsr.Count -gt 1) { Write-Error "UPN Conflict in Msol V1 Service About ""$($objMfa.UserPrincipalName)"""; $PSCmdlet.WriteObject($objMfa); Continue; } } ElseIf ("$($InputObject[$intI])" -match "$([SnsPsModule.SnsPatterns]::GUIDPattern)") { ##### Assign The ObjectId Property To The Corresponding Object Property $objMfa.ObjectId = "$($InputObject[$intI])"; ##### Query The Msol V1 About User With UPN Write-Debug "Query The Msol V1 About User With ObjectId: ""$($objMfa.ObjectId)"""; [Microsoft.Online.Administration.User[]]$arrMsolUsr = @(); [Microsoft.Online.Administration.User[]]$arrMsolUsr = Get-MsolUser -ObjectId "$($objMfa.ObjectId)" -Verbose:$false -Debug:$false; If ($arrMsolUsr.Count -eq 1) { $objMfa.MsolUser = $arrMsolUsr[0] | Select-Object -Property * -Verbose:$false -Debug:$false; } ElseIf ($arrMsolUsr.Count -lt 1) { Write-Error "Unable To Find MsolUser With ObjectId: ""$($objMfa.ObjectId)"""; $PSCmdlet.WriteObject($objMfa); Continue; } ElseIf ($arrMsolUsr.Count -gt 1) { Write-Error "ObjectId Conflict in Msol V1 Service About ""$($objMfa.ObjectId)"""; $PSCmdlet.WriteObject($objMfa); Continue; } } #================================================================================== #endregion Retrieve MsolUser Object From AzureAD #================================================================================== #================================================================================== #region Calculate The Initial State #================================================================================== ##### Continue If The MsolUser Is Generated Write-Debug "Continue If The MsolUser Is Generated"; If ("$(($objMfa.MsolUser | Get-Member -Verbose:$false -Debug:$false)[0].TypeName)" -eq "Selected.Microsoft.Online.Administration.User") { ##### Evaluate The Current Object State Write-Debug "Evaluate The Current Object State"; $objMfa.UserPrincipalName = "$($objMfa.MsolUser.UserPrincipalName)"; $objMfa.DisplayName = "$($objMfa.MsolUser.DisplayName)"; $objMfa.ObjectId = "$($objMfa.MsolUser.ObjectId.Guid)"; $objMfa.MfaState = "$($objMfa.MsolUser.StrongAuthenticationRequirements[0].State)"; $objMfa.ValueCorrect = ( ` ((-not $Force.IsPresent) -and (-not -not "$($objMfa.MfaState)")) ` -or ` (($Force.IsPresent) -and ("$($objMfa.MfaState)" -eq "$($objAuthRequirement.State)")) ` ); Write-Verbose "Found UPN: ""$($objMfa.UserPrincipalName)"" ObjectId: ""$($objMfa.ObjectId)"" MfaState: ""$($objMfa.MfaState)"""; } #================================================================================== #endregion Calculate The Initial State #================================================================================== #================================================================================== #region Enable-SnsMfa #================================================================================== ##### Continue If The MsolUser Is Retrieved Successfully Write-Debug "Continue If The MsolUser Is Retrieved Successfully" If ("$(($objMfa.MsolUser | Get-Member -Verbose:$false -Debug:$false)[0].TypeName)" -eq "Selected.Microsoft.Online.Administration.User") { ##### Verify Whether The MFA Is Not Enabled Write-Debug "Verify Whether The MFA Is Not Enabled"; If (-not $objMfa.ValueCorrect) { ##### Invoke ShouldProcess Method Write-Debug "Invoke ShouldProcess Method"; If ($PSCmdlet.ShouldProcess("$($objMfa.UserPrincipalName)")) { ##### Loop The MFA Enforcement Write-Debug "Loop The MFA Enforcement"; [System.Int32]$intIn = 0; While ((-not $objMfa.ValueCorrect) -and ($intIn -lt $Attempts)) { ##### Enforce MFA To The User Write-Verbose "$($objAuthRequirement.State) MFA To: ""$($objMfa.UserPrincipalName)"""; Set-MsolUser -ObjectId "$($objMfa.ObjectId)" -StrongAuthenticationRequirements $objAuthRequirement -Verbose:$false -Debug:$false | Out-Null; ##### Process The Loop Variables Write-Debug "Process The Loop Variables"; [System.Int32]$intIn = $intIn + 1; $objMfa.ValueModified = $true; $objMfa.MsolUser = $null; $objMfa.MfaState = ""; $objMfa.ValueCorrect = $false; Start-Sleep -Seconds 1 -Verbose:$false -Debug:$false; ##### Query The Msol V1 About User With UPN Write-Debug "Query The Msol V1 About User With UPN: ""$($objMfa.UserPrincipalName)"""; [Microsoft.Online.Administration.User[]]$arrMsolUsr = @(); [Microsoft.Online.Administration.User[]]$arrMsolUsr = Get-MsolUser -ObjectId "$($objMfa.ObjectId)" -Verbose:$false -Debug:$false; ##### Verify The Msol V1 Query Output Write-Debug "Verify The Msol V1 Query Output"; If ($arrMsolUsr.Count -eq 1) { ##### Update The Object Properties Write-Debug "Update The Object Properties"; $objMfa.MsolUser = $arrMsolUsr[0] | Select-Object -Property * -Verbose:$false -Debug:$false; $objMfa.MfaState = "$($objMfa.MsolUser.StrongAuthenticationRequirements[0].State)"; $objMfa.ValueCorrect = ( ` ((-not $Force.IsPresent) -and (-not -not "$($objMfa.MfaState)")) ` -or ` (($Force.IsPresent) -and ("$($objMfa.MfaState)" -eq "$($objAuthRequirement.State)")) ` ); } } } } Else { Write-Verbose "The MFA Of User: ""$($objMfa.UserPrincipalName)"" Is Already: ""$($objMfa.MfaState)"""; } } #================================================================================== #endregion Enable-SnsMfa #================================================================================== ##### Pass The Output Object To The Pipeline Write-Debug "Pass Output Object To The Pipeline"; $PSCmdlet.WriteObject($objMfa); } If ($bolProgrBar) { Write-Progress -Activity "Enable-SnsMfa" -Id 1 -PercentComplete 100 -Verbose:$false -Debug:$false; Write-Progress -Activity "Enable-SnsMfa" -Id 1 -Completed -Verbose:$false -Debug:$false; } } } ##### Override The End Method End { Write-Debug "Override End Method"; Write-Verbose ""; ##### Reset The Variables Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "intIn"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "arrMsolUsr"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "objMfa"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "intI"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "bolProgrBar"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "bolInteractive"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "objAuthRequirement"; $PSCmdlet.MyInvocation.BoundParameters.Keys | ForEach { Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "$($_)"; } ##### Stop The StopWatch $objCmdStopWatch.Stop(); Write-Verbose "Command Elapsed: ""$($objCmdStopWatch.ElapsedMilliseconds)"" Milliseconds." ; Write-Verbose "End!"; } } ##### Export-SnsCredentialFile ==================================================== Function Export-SnsCredentialFile () { <# .SYNOPSIS This CmdLet Creates Encrypted Password File. .DESCRIPTION This CmdLet Creates Encrypted Password File. The CmdLet Is Intended To Interactively Prepare Credential File Of Another Account For Further Usage By Scripts Executed As A Service On Schedule. CmdLet Uses A Graphical User Interface (GUI) For Better User Experience. The Produced Credential File Contains Information About When And Who Created It. WARNING: The Produced Encrypted Credential Files Can Be Decrypted Only Within The Security Context In Which They Are Created And Only On The Machine They Are Created. With Other Words Only The Person Who Have Created A File Can Decrypt It On The Same Machine Only. .INPUTS Interactive Input Via Graphical User Interface. .OUTPUTS Encrypted Secure Password File. .NOTES AUTHOR: Svetoslav Nedyalkov Savov THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER. .EXAMPLE Export-SnsCredentialFile; .LINK svesavov / SnsPsModule - https://github.com/svesavov/SnsPsModule .LINK Svetoslav Savov on LinkedIn - https://www.linkedin.com/in/svetoslavsavov #> [CmdletBinding()] Param () ##### Override The Begin Method Begin { Write-Debug "Override Begin Method"; ##### Load The Asemblies And Initialize The Variables Write-Debug "Load The Asemblies And Initialize The Variables"; [Void][Reflection.Assembly]::Load('System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'); [System.Management.Automation.PSCredential]$objCredentials = $null; [System.String]$strEncryptedPass = ""; [System.Windows.Forms.SaveFileDialog]$objSaveFileDialog = $null; [System.String]$strPath = ""; ##### Launch Get-Credential Write-Debug "Launch Get-Credential"; [System.Management.Automation.PSCredential]$objCredentials = $null; [System.Management.Automation.PSCredential]$objCredentials = Get-Credential -Verbose:$false -Debug:$false; [System.String]$strEncryptedPass = ""; [System.String]$strEncryptedPass = $objCredentials.Password | ConvertFrom-SecureString -Verbose:$false -Debug:$false; ##### Launch Save File Dialog Write-Debug "Launch Save File Dialog"; [System.Windows.Forms.SaveFileDialog]$objSaveFileDialog = $null; [System.Windows.Forms.SaveFileDialog]$objSaveFileDialog = New-Object -TypeName "System.Windows.Forms.SaveFileDialog" -Verbose:$false -Debug:$false; $objSaveFileDialog.AddExtension = $true; $objSaveFileDialog.DefaultExt = "ini"; $objSaveFileDialog.FileName = "$(""$($objCredentials.UserName)"".Replace('\','@@')).ini"; $objSaveFileDialog.Title = "Save Secure Password File."; $objSaveFileDialog.Filter = "Information Configuration File (*.ini)|*.ini|All files (*.*)|*.*"; $objSaveFileDialog.ValidateNames = $true; $objSaveFileDialog.ShowDialog(); [System.String]$strPath = ""; [System.String]$strPath = "$($objSaveFileDialog.FileName)"; ##### Export The Encrypted Pass String On Credential File Write-Debug "Export The Encrypted Pass String On Credential File"; If ([SnsPsModule.SnsCredentialFile]::Export("$($strEncryptedPass)", "$($strPath)")) { ##### Create And Launch The Message Window Write-Debug "Create And Launch The Message Window"; [Void][System.Windows.Forms.MessageBox]::Show( ` "Password Is Exported In:`r`n$($strPath)", "Information", [System.Windows.Forms.MessageBoxButtons]::"OK", [System.Windows.Forms.MessageBoxIcon]::"Information"); ##### } Else { ##### Create And Launch The Message Window Write-Debug "Create And Launch The Message Window"; [Void][System.Windows.Forms.MessageBox]::Show( ` "Failed To Export, Or The Export Contains Errors:`r`n$($strPath)", "Error", [System.Windows.Forms.MessageBoxButtons]::"OK", [System.Windows.Forms.MessageBoxIcon]::"Error"); ##### } ##### Reset The Variables Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strPath"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "objSaveFileDialog"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strEncryptedPass"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "objCredentials"; [System.GC]::Collect(); Exit 0; Stop-Process -Id $pid -Force:$true -Confirm:$false -Verbose:$false -Debug:$false; } } ##### Get-SnsAdGroupMembers ======================================================= Function Get-SnsAdGroupMembers () { <# .SYNOPSIS CmdLet Designed To Expand Active Directory Group Members Including The Members Of The Nested Groups. .DESCRIPTION CmdLet Designed To Expand Active Directory Group Members Including The Members Of The Nested Groups. The AD Group Nesting Could Lead To Strange Results Like For Example A Group To Be A Member Of Another Group And The Second Group To Be Members Of The First Group. With Groups Nesting Could Have Very Complex Scenarios. If They Are Not Considered The CmdLet Can Loop Itself Into Endless Loop. In Order To Handle That The CmdLet Have Internal Logic Which Keep Track Of The Already Expanded Groups, And In Case Already Expanded Group Is Reached The Loop Is Broken To Continue With The Next Nested Group Which Have To Be Expanded If Any. For That Purpose The CmdLet Have An Input Parameter "InnerLoop" Which Have To Be Never Used By The User. It Is Specified By The CmdLet Whenever It Is Called By Itself. Using The Parameter By The User Will Not Lead To Endless Loops. It Will Lead To Missing Group Members In The End Result Because Some Groups Will Not Be Expanded. The CmdLet Keep Track Of The Already Expanded Groups In A Global Variable. In Case The Parameter "InnerLoop" Is Specified This Global Variable Will Not Be Cleared Whenever The CmdLet Start. Which Will Result In Skipping Of The Groups Expansion Of The Groups Processed Previous time When The CmdLet Is Executed If Any. .PARAMETER Group Specifies Active Directory Group Identity. Parameter Alias: N/A Parameter Validation: N/A .PARAMETER InputProperty Specifies Active Directory Attribute Which Value Is Used As Group Identity. This Is The Attribute Name As Displayed In ADSIEdit Console. Parameter Alias: N/A Parameter Validation: N/A .PARAMETER OutputProperty Specifies The AD Attribute Which Will Be Used To Provide The Group Members. This Is The Attribute Name As Displayed In ADSIEdit Console. Use An AD Attribute Which Is Unique In The Active Directory To Avoid Confusions. Parameter Alias: N/A Parameter Validation: N/A .PARAMETER DomainController Specifies Fully Qualified Domain Name Of A Domain Controller. It Is Used To Specify To The CmdLet To Execute All LDAP Queries To A Single Machine To Avoid Any AD Replication Issues Parameter Alias: N/A Parameter Validation: Yes Using Querying AD Forest About Global Catalog Servers .PARAMETER InnerLoop This Parameter Shall Be Never Used. Parameter Alias: N/A Parameter Validation: N/A .INPUTS Pipeline Input -Group Which Accept Values Of Type [System.String[]]. .OUTPUTS Pipeline Output [SnsPsModule.SnsAdGroupMemberObject[]] Which Contains Output About The Specified Group Along With A List With All Member User Account As Object Property. .NOTES AUTHOR: Svetoslav Nedyalkov Savov THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER. .EXAMPLE [SnsPsModule.SnsAdGroupMemberObject[]]$arrAdGrps = Get-SnsAdGroupMembers -Group "It Team" ` -InputProperty "cn" -OutputProperty "userPrincipalName"; .LINK svesavov / SnsPsModule - https://github.com/svesavov/SnsPsModule .LINK Svetoslav Savov on LinkedIn - https://www.linkedin.com/in/svetoslavsavov #> [CmdletBinding(PositionalBinding = $false)] Param ( [Parameter(Mandatory = $true, ` ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] [ValidateNotNullOrEmpty()] [System.String[]]$Group, [Parameter(Mandatory = $false, ` ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateNotNullOrEmpty()] [System.String[]]$InputProperty = @('distinguishedName'), [Parameter(Mandatory = $false, ` ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateNotNullOrEmpty()] [System.String]$OutputProperty = 'userPrincipalName', [Parameter(Mandatory = $false, ` ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateNotNullOrEmpty()] [ValidateScript({ ` ((((([System.Directoryservices.Activedirectory.Domain]::GetCurrentDomain()).Forest).GlobalCatalogs) | ` Select-Object -ExpandProperty "Name") -icontains "$_") })] [System.String]$DomainController = "", [Parameter(Mandatory = $false, ` ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [Switch]$InnerLoop ) ##### Override The Begin Method Begin { Write-Debug "Override Begin Method"; Write-Verbose ""; Write-Verbose "Get-SnsAdGroupMembers"; #Write-Verbose "ParameterSetName: $($PSCmdlet.ParameterSetName)`r`n"; ##### Initialize New Measure Watch [System.Diagnostics.Stopwatch]$objCmdStopWatch = [System.Diagnostics.Stopwatch]::StartNew(); If ("$((Get-FileHash -Path ""$($PSCommandPath)"" -Algorithm ""SHA256"" -Verbose:$false -Debug:$false).Hash)" -ne "$($global:SnsModuleCfg.ModuleHash)") { Write-Warning "There Is New Version Of SnsPsModule Module Released. Please Restart The PowerShell Session." -WarningAction "Continue"; }; [System.String]$strExc = "This CmdLet Is Deprecated. Please Use ""Search-SnsAdObject"" CmdLet With LDAP Query ""memberOf:1.2.840.113556.1.4.1941:=<YourGroupDN>"" As It Is Described Here https://bit.ly/2RFdUr2."; Write-Error $strExc -ErrorAction "Stop"; #================================================================================== #region Define The Output Object TypeName #================================================================================== ##### Define The [SnsPsModule.SnsMfaStatus] Object Write-Debug "Define The [SnsPsModule.SnsAdGroupMemberObject] Object"; Add-Type ` @" using System; namespace SnsPsModule { public class SnsAdGroupMemberObject { public SnsAdGroupMemberObject() { Group = ""; InputProperty = ""; distinguishedName = ""; Members = new string[0]; } public string Group { get; set; } public string InputProperty { get; set; } public string distinguishedName { get; set; } public string[] Members { get; set; } } } "@ ##### ##### Search-SnsAdObjectHelper ==================================================== Function Search-SnsAdObjectHelper () { <# .SYNOPSIS CmdLet Designed To Be Used As Internal Helper Function To Query AD. .DESCRIPTION CmdLet Designed To Be Used As Internal Helper Function To Query AD. .PARAMETER LdapQuery Specifies The AD Attribute Which Will Be Used To Provide The Group Members. This Is The Attribute Name As Displayed In ADSIEdit Console. Use An AD Attribute Which Is Unique In The Active Directory To Avoid Confusions. Parameter Alias: N/A Parameter Validation: N/A .PARAMETER DomainController Specifies Fully Qualified Domain Name Of A Domain Controller. It Is Used To Specify To The CmdLet To Execute All LDAP Queries To A Single Machine To Avoid Any AD Replication Issues Parameter Alias: N/A Parameter Validation: Yes Using Querying AD Forest About Global Catalog Servers .INPUTS The CmdLet Does Not Support Pipeline Input. .OUTPUTS The CmdLet Does Not Support Pipeline Output. .NOTES AUTHOR: Svetoslav Nedyalkov Savov THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER. .EXAMPLE [System.String[]]$arrOut = Search-SnsAdObjectHelper -LdapQuery $LdapQuery ` -DomainController $DomainController; .LINK svesavov / SnsPsModule - https://github.com/svesavov/SnsPsModule .LINK Svetoslav Savov on LinkedIn - https://www.linkedin.com/in/svetoslavsavov #> [CmdletBinding(PositionalBinding = $false)] Param ( [Parameter(Mandatory = $true, ` ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateNotNullOrEmpty()] [System.String]$LdapQuery, [Parameter(Mandatory = $true, ` ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [AllowEmptyString()] [System.String]$DomainController ) ##### Override The Begin Method Begin { Write-Debug "Override Begin Method"; Write-Verbose ""; Write-Verbose "Search-SnsAdObjectHelper"; ##### Initialize The Variables [System.Collections.Specialized.OrderedDictionary]$hshSplat = @{}; [System.DirectoryServices.SearchResult[]]$arrSrchRes = @(); [System.String[]]$arrOutput = @(); [System.Int32]$intI = 0; ##### Generate AD Search Splatting Hashtable Write-Debug "Generate AD Search Splatting Hashtable"; [System.Collections.Specialized.OrderedDictionary]$hshSplat = @{}; $hshSplat.Add("LdapQuery", "$($LdapQuery)"); $hshSplat.Add("DcSearch", $true); $hshSplat.Add("ReturnProperties", @("distinguishedName")); $hshSplat.Add("Verbose", $false); $hshSplat.Add("Debug", $false); ##### Verify Whether DomainController Is Provided Write-Debug "Verify Whether DomainController Is Provided"; If (-not -not "$($DomainController)") { ##### Add The Domain Controller To The Splatting Write-Debug "Add $($DomainController) DomainController To The Splatting"; $hshSplat.Add("DomainController", "$($DomainController)"); }; ##### Run LDAP Search Against AD Write-Debug "Run LDAP Search Against AD"; [System.DirectoryServices.SearchResult[]]$arrSrchRes = @(); [System.DirectoryServices.SearchResult[]]$arrSrchRes = Search-SnsAdObject @hshSplat; ##### Convert The AD Search Results To Collection Of Strings Write-Debug "Convert The AD Search Results To Collection Of Strings"; [System.String[]]$arrOutput = @(); If ($arrSrchRes.Count -gt 0) { ##### Loop Each Search Result Write-Debug "Loop Each Search Result"; [System.Int32]$intI = 0; For ([System.Int32]$intI = 0; $intI -lt $arrSrchRes.Count; $intI++) { ##### Add To The Output Array The DistinguishedName As A String Write-Debug "Add To The Output Array The DistinguishedName As A String"; [System.String[]]$arrOutput += "$($arrSrchRes[$intI].Properties.distinguishedname)"; } } ##### Reset The Variables Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "intI"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "arrSrchRes"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "hshSplat"; $PSCmdlet.MyInvocation.BoundParameters.Keys | ForEach { Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "$($_)"; } Return $arrOutput; } } #================================================================================== #endregion Define The Output Object TypeName #================================================================================== ##### Initialize The Variables [System.String]$strDcTemp = ""; [System.UInt32]$intI = 0; [System.String[]]$arrGroups = @(); [System.String[]]$arrRawMembers = @(); [System.String[]]$arrMembers = @(); [System.UInt32]$intIn = 0; [System.DirectoryServices.DirectoryEntry]$objAdsi = $null; [System.Collections.Specialized.OrderedDictionary]$hshSplat = @{}; [System.String[]]$arrTemp = @(); [System.Int32]$intInd = 0; [SnsPsModule.SnsAdGroupMemberObject]$objOutput = $null; ##### Generate The ADSI String For DomainController Write-Debug "Generate The ADSI String For DomainController"; [System.String]$strDcTemp = ""; If (-not -not "$($DomainController)") { [System.String]$strDcTemp = "$($DomainController)/"; }; ##### Verify Whether The CmdLet Is Not Called From Itself Write-Debug "Verify Whether The CmdLet Is Not Called From Itself" If (-not $InnerLoop.IsPresent) { ##### Reset The Loop Variable [System.String[]]$global:ArrInnerLoop = @(); }; } ##### Override The Process Method Process { Write-Debug "Override Process Method"; Write-Verbose ""; ##### Process Each Input Object Write-Debug "Process Each Input Object"; [System.UInt32]$intI = 0; For ([System.UInt32]$intI = 0; $intI -lt $Group.Count; $intI++) { #================================================================================== #region Search About The AD Group #================================================================================== ##### Process Each Identity Property Write-Debug "Process Each Identity Property"; [System.String[]]$arrGroups = @(); [System.String[]]$arrRawMembers = @(); [System.String[]]$arrMembers = @(); [System.UInt32]$intIn = 0; For ([System.UInt32]$intIn = 0; $intIn -lt $InputProperty.Count; $intIn++) { ##### Add The Search Output To A Temporary Groups Collection ##### Since There Could Be More Than One Identity Properties Specified ##### It Is Possible A Single Group To Be Reverted Several Times ##### For Example If CN And displayName Values Are Equal ##### And We Have Both Attributes Specified As Identity Property ##### From That Perspective We Need To Add All The Search Results ##### Into A Single Collection Then Filter To Unique ##### And Only Then To Verify The Search Result Count Write-Debug "Add The Search Output To A Temporary Groups Collection" [System.String[]]$arrGroups += Search-SnsAdObjectHelper ` -LdapQuery "(&(objectClass=group)($($InputProperty[$intIn])=$($Group[$intI])))" ` -DomainController $DomainController -Verbose:$false -Debug:$false; ##### } ##### Filter The Search Results To Unique Write-Debug "Filter The Search Results To Unique"; [System.String[]]$arrGroups = $arrGroups | Select-Object -Unique:$true -Verbose:$false -Debug:$false; ##### Verify The Group Search Result Write-Debug "Verify The Group Search Result"; [System.String[]]$arrRawMembers = @(); [System.String[]]$arrMembers = @(); If ($arrGroups.Count -eq 1) { ##### Add The Group To The Global Verification Array Preventing Endless Loops Write-Debug "Add The Group To The Global Verification Array Preventing Endless Loops"; [System.String[]]$global:ArrInnerLoop += "$($arrGroups[0])"; ##### Get The Group Members ##### Since Active Directory Service Have Limitations About The Number Of Values Reverted Inside Multivalued Attribute ##### We have To Query The Active Directory About The Objects Which Are Members Of The Group ##### Instead To Get The "Member" Property Of The Group Itself ##### The Limitation Is Different And Depends From The Active Directory Version ##### When The Root Domain Was Built ##### Adprep Does Not Modify The Limitation ##### For AD 2003 The Limitation Is 1500 ##### For AD 2008 The Limitation Is 5000 ##### Have No Access To Directory Services Build Directly With Newer Version To Check The Limitation ##### All AD Services That I Have Access Are Upgraded From 2003 Or 2008 ##### The Limitation Can Be Modified From The Domain Partition ##### However It Requires Domain Administrator Access Rights And Can Be Risky If You Don't Know What Are You Doing. ##### It Is Preferable To Take The Other Way Round And Query The AD About Objects That Are Members Of Our Group. Write-Debug "Get The Group Members"; [System.String[]]$arrRawMembers += Search-SnsAdObjectHelper ` -LdapQuery "memberOf=$($arrGroups[0])" ` -DomainController $DomainController -Verbose:$false -Debug:$false; ##### } ElseIf ($arrGroups.Count -lt 1) { [System.Array]$arrRawMembers = @(); Write-Verbose "Unable To find Group ""$($Group[$intI])""."; [System.String[]]$arrMembers += "MissingGroup"; } Else { [System.Array]$arrRawMembers = @(); Write-Verbose ('Found more than One Group "' + $GroupIdentity + '".'); [System.String[]]$arrMembers += 'MultipleGroups'; } #================================================================================== #endregion Search About The AD Group #================================================================================== #================================================================================== #region Process Each Group Member #================================================================================== ##### Continue If There Are Group Members Write-Debug "Continue If There Are Group Members"; If ($arrRawMembers.Count -gt 0) { ##### Process Each Retrieved Group Member Write-Debug "Process Each Retrieved Group Member" [System.UInt32]$intIn = 0; For ([System.UInt32]$intIn = 0; $intIn -lt $arrRawMembers.Count; $intIn++) { ##### Generate The Group Member ADSI Object Write-Debug "Generate The Group Member ADSI Object"; [System.DirectoryServices.DirectoryEntry]$objAdsi = $null; [System.DirectoryServices.DirectoryEntry]$objAdsi = [ADSI]"LDAP://$($strDcTemp)$($arrRawMembers[$intIn])"; ##### Verify The Group Member ADSI Object Type Write-Debug "Verify The Group Member ADSI Object Type"; If ( ` ("$($objAdsi.objectCategory)" -like "CN=Person,CN=Schema,CN=Configuration,DC=*") ` -and ` ("$($objAdsi.objectClass)" -like "*user*") ` ) { ##### This Is A User Object Write-Debug "User Object: ""$($objAdsi.sAMAccountName)"""; ##### Check Whether There Are No Errors From The Previous Group Members Write-Debug "Check Whether There Are No Errors From The Previous Group Members"; If ( ` ($arrMembers -inotcontains "MissingGroup") ` -and ` ($arrMembers -inotcontains "MultipleGroups") ` ) { ##### Adding The User To The Output Array Write-Verbose "User: $($objAdsi.""$($OutputProperty)"")"; [System.Array]$arrMembers += "$($objAdsi.""$($OutputProperty)"")"; } } ElseIf ("$($objAdsi.objectClass)" -like "*group*") { ##### This Is A Group Object Write-Verbose "Group Object: ""$($objAdsi.cn)"""; ##### Verify Whether The Group Is Not Already Processed If ($global:ArrInnerLoop -inotcontains "$($arrRawMembers[$intIn])") { ##### Generate Nested Get-SnsAdGroupMembers Splatting Write-Debug "Generate Nested Get-SnsAdGroupMembers Splatting"; [System.Collections.Specialized.OrderedDictionary]$hshSplat = @{}; $hshSplat.Add("Group", "$($arrRawMembers[$intIn])"); $hshSplat.Add("InputProperty", @('distinguishedName')); $hshSplat.Add("OutputProperty", $OutputProperty); $hshSplat.Add("Verbose", $Verbose.IsPresent); $hshSplat.Add("Debug", $false); $hshSplat.Add("InnerLoop", $true); ##### Verify Whether DomainController Is Provided Write-Debug "Verify Whether DomainController Is Provided"; If (-not -not "$($DomainController)") { ##### Add The Domain Controller To The Splatting Write-Debug "Add $($DomainController) DomainController To The Splatting"; $hshSplat.Add("DomainController", "$($DomainController)"); }; ##### Query About The Nested Group Members Write-Debug "Query About The Nested Group Members"; [System.String[]]$arrTemp = @(); [System.String[]]$arrTemp = Get-SnsAdGroupMembers @hshSplat | ` Select-Object -ExpandProperty "Members" -Verbose:$false -Debug:$false; ##### ##### Verify The Query Output Write-Debug "Verify The Query Output"; If ($arrTemp -icontains "MissingGroup") { ##### Failed To Query The Group Write-Verbose "Missing Group: ""$($objAdsi.cn)""."; [System.String[]]$arrMembers = @(); [System.String[]]$arrMembers = @("MissingGroup"); } ElseIf ($arrTemp -icontains "MultipleGroups") { Write-Verbose "Multiple groups: ""$($arrRawMembers[$intIn])""."; [System.String[]]$arrMembers = @(); [System.String[]]$arrMembers = @("MultipleGroups"); } ElseIf ($arrTemp -icontains 'EmptyGroup') { ##### Doing Nothing It Is Not An Issue That Nested Group Is Empty. Write-Verbose "Empty Group: ""$($objAdsi.cn)""."; } Else { ##### Add The Nested Group Members To The Output Array Write-Verbose "Add ""$($objAdsi.cn)"" Members To The Output Array"; [System.Int32]$intInd = 0; For ([System.Int32]$intInd = 0; $intInd -lt $arrTemp.Count; $intInd++) { [System.String[]]$arrMembers += "$($arrTemp[$intInd])"; } } } } } } #================================================================================== #endregion Process Each Group Member #================================================================================== #================================================================================== #region Prepare The Output Object #================================================================================== ##### Filter To Only Unique Members ##### Some Members Could Be Members Of Nested Groups As Well ##### Therefore Could Be Present More Than Once Write-Debug "Filter To Only Unique Members"; [System.String[]]$arrMembers = $arrMembers | ` Select-Object -Unique:$true -Verbose:$true -Debug:$false; ##### ##### Verify The Results Write-Debug "Verify The Results"; If ($arrMembers.Count -le 0) { ##### The Group Is Empty [System.String[]]$arrMembers = @(); [System.String[]]$arrMembers += 'EmptyGroup'; Write-Verbose "Empty Group ""$($Group[$intI])"""; } ##### Generate The Output Object Write-Debug "Generate The Output Object"; [SnsPsModule.SnsAdGroupMemberObject]$objOutput = $null; [SnsPsModule.SnsAdGroupMemberObject]$objOutput = New-Object ` -TypeName "SnsPsModule.SnsAdGroupMemberObject" ` -Verbose:$false -Debug:$false; ##### ##### Add The Output Object Properties $objOutput.Group = "$($Group[$intI])"; $objOutput.InputProperty = $InputProperty; $objOutput.distinguishedName = "$($arrGroups[0])"; $objOutput.Members = $arrMembers; ##### Pass The Output Object To The Pipeline Write-Debug "Pass Output Object To The Pipeline"; $PSCmdlet.WriteObject($objOutput); #================================================================================== #endregion Prepare The Output Object #================================================================================== } } ##### Override The End Method End { Write-Debug "Override End Method"; Write-Verbose ""; ##### Reset The Variables Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "objOutput"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "intInd"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "arrTemp"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "hshSplat"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "objAdsi"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "intIn"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "arrMembers"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "arrRawMembers"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "arrGroups"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "intI"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strDcTemp"; $PSCmdlet.MyInvocation.BoundParameters.Keys | ForEach { Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "$($_)"; } ##### Stop The StopWatch $objCmdStopWatch.Stop(); Write-Verbose "Command Elapsed: ""$($objCmdStopWatch.ElapsedMilliseconds)"" Milliseconds." ; Write-Verbose "End!"; } } ##### Import-SnsCredentialFile ==================================================== Function Import-SnsCredentialFile () { <# .SYNOPSIS This CmdLet Import An Encrypted Credential File Convert The Imported Value To PSCredentials Object And Verifies The Output Existence .DESCRIPTION This CmdLet Import An Encrypted Credential File Convert The Imported Value To PSCredentials Object And Verifies The Output Existence In Case The Credential Object Generation Fail The CmdLet Can Log An Event In The Windows Event Viewer Application Log And Kill The Script Process This Functionality Is Enabled Automatically When EventSource Parameter Is Provided Simple Throwing Terminating Error Will Keep The PowerShell Process Which Will Prevent The Next Script Instances From Execution And Any Possible Script Monitoring Will Be Cheated That The Script Is Still Running The CmdLet Can Import And Decrypt Properly Only Files Created Within The Security Context Of The Account Which Executes The Function And Only On The Same Machine Where The File Was Created The CmdLet Have Two Parameter Set: -- UserAndFolder Unlike The Name Here Have To Be Specified The UserName And The Full Absolute UNC Folder Path Where The Encrypted Password File Resides -- FullPath Here Have To Be Provided The Full Absolute UNC Path To The Credential File. The CmdLet Will Try To Generate The UserName From The FileName .PARAMETER UserName Specifies The UserName Parameter Set: UserAndFolder Parameter Alias: User, Usr, UID, ID, Identity, FileName, Name Parameter Validation: N/A .PARAMETER FolderPath Specifies The Full Absolute UNC Folder Path Where The Credential File Resides Parameter Set: UserAndFolder Parameter Alias: Folder, FolderName, UNCPath, FolderFullName Parameter Validation: Folder Existence Validation .PARAMETER FilePath Specifies The Full Absolute UNC Path To The Credential File Parameter Set: FullPath Parameter Alias: FullPath, FileFullPath, FileUNCPath Parameter Validation: Yes, File Existence And File Name Validation .PARAMETER EventSource Specifies The Application Log Event Source To Be Used For The Error Event Logging Parameter Set: All Parameter Alias: ScriptName Parameter Validation: N/A .INPUTS Global Variable [System.String]$global:StrScriptName - Contains The Name Of The Script To Be Used As Event Source .OUTPUTS [System.Management.Automation.PSCredential] Which Contains The PSCredential .NOTES AUTHOR: Svetoslav Nedyalkov Savov THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER. .EXAMPLE [System.Management.Automation.PSCredential]$objCredential = Import-SnsCredentialFile ` -UserName 'john.smith@contoso.com' -FolderPath 'C:\'; .EXAMPLE [System.Management.Automation.PSCredential]$objCredential = Import-SnsCredentialFile ` -FilePath 'C:\john.smith@contoso.com.ini'; .LINK svesavov / SnsPsModule - https://github.com/svesavov/SnsPsModule .LINK Svetoslav Savov on LinkedIn - https://www.linkedin.com/in/svetoslavsavov #> [CmdletBinding(PositionalBinding = $false)] Param( [Parameter(Mandatory = $true, ParameterSetName = "UserAndFolder", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $true)] [Alias("User", "Usr", "UID", "ID", "Identity", "FileName", "Name")] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateNotNullOrEmpty()][System.String[]]$UserName, [Parameter(Mandatory = $true, ParameterSetName = "UserAndFolder", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $true)] [Alias("Folder", "FolderName", "UNCPath", "FolderFullName")] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateScript({("$($_)".Contains("\"))})] [ValidateScript({([System.IO.Directory]::Exists("$($_)"))})] [ValidateNotNullOrEmpty()][System.String]$FolderPath, [Parameter(Mandatory = $true, ParameterSetName = "FullPath", ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $false)] [Alias("FullPath", "FileFullPath", "FileUNCPath")] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateScript({("$($_)".EndsWith(".ini"))})] [ValidateScript({("$($_)".Contains("\"))})] [ValidateScript({([System.IO.File]::Exists("$($_)"))})] [ValidateNotNullOrEmpty()][System.String[]]$FilePath, [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [Alias("ScriptName")] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateScript({((-not "$($_)") -or [SnsPsModule.SnsEventLog]::VerifySnsEventSource("$($_)"))})] [AllowNull()][AllowEmptyString()][System.String]$EventSource ) ##### Override The Begin Method Begin { Write-Debug "Override Begin Method"; Write-Verbose ""; Write-Verbose "Import-SnsCredentialFile"; Write-Verbose "ParameterSetName: $($PSCmdlet.ParameterSetName)"; Write-Verbose "ErrorAction: $($ErrorActionPreference)"; ##### Initialize New Measure Watch [System.Diagnostics.Stopwatch]$objCmdStopWatch = [System.Diagnostics.Stopwatch]::StartNew(); If ("$((Get-FileHash -Path ""$($PSCommandPath)"" -Algorithm ""SHA256"" -Verbose:$false -Debug:$false).Hash)" -ne "$($global:SnsModuleCfg.ModuleHash)") { Write-Warning "There Is New Version Of SnsPsModule Module Released. Please Restart The PowerShell Session." -WarningAction "Continue"; }; ##### Initialize The Variables [System.Collections.Specialized.OrderedDictionary]$hashTemp = [Ordered]@{}; [System.Int32]$intI = 0; [System.String]$strTemp = ""; [System.Management.Automation.PSCredential]$objCredentials = $null; [System.Security.SecureString]$secStrPassword = $null; } ##### Override The Process Method Process { Write-Debug "Override Process Method"; Write-Verbose ""; #================================================================================== #region Generate The FilePath Hash #================================================================================== ##### Check The ParameterSetName Write-Debug "Check The ParameterSetName"; [System.Collections.Specialized.OrderedDictionary]$hashTemp = [Ordered]@{}; Switch ("$($PSCmdlet.ParameterSetName)") { "UserAndFolder" { ##### Normalize The FilePath Value Write-Debug "Normalize The FilePath Value"; [System.String]$FolderPath = "$($FolderPath.TrimEnd(""\""))\"; ##### Verify The UserName Input Write-Debug "Verify The UserName Input"; If ($UserName.Count -gt 0) { ##### Loop The Provided UserNames Write-Debug "Loop The Provided UserNames"; [System.Int32]$intI = 0; For ([System.Int32]$intI = 0; $intI -lt $UserName.Count; $intI++) { [System.String]$strTemp = ""; [System.String]$strTemp = "$($FolderPath)$($UserName[$intI].Replace(""\"",""@@"")).ini"; ##### Verify Whether A Credential File For The Specified User Exists Write-Debug "Verify Whether A Credential File For The Specified User Exists"; If (Test-Path -Path "$($strTemp)" -PathType "Leaf" -Verbose:$false -Debug:$false) { Write-Verbose "Enumerated Credential File: ""$($strTemp)""."; $hashTemp.Add("$($UserName[$intI])", "$($strTemp)"); } Else { [System.String]$strEventMessage = "Enumerated Credential File ""$($strTemp)"" Does Not Exists."; Log-SnsEventLogMessageHelper -Message "$($strEventMessage)" -EventLogEntryType "Error" -EventSource "$($EventSource)" -Verbose:$false -Debug:$false; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strEventMessage"; Return; } } } } "FullPath" { ##### Verify The FilePath Input Write-Debug "Verify The FilePath Input"; If ($FilePath.Count -gt 0) { ##### Loop The Provided FilePaths Write-Debug "Loop The Provided FilePaths"; [System.Int32]$intI = 0; For ([System.Int32]$intI = 0; $intI -lt $FilePath.Count; $intI++) { ##### Enumerate The UserName Write-Debug "Enumerate The UserName"; [System.String]$strTemp = ""; [System.String]$strTemp = "$($FilePath[$intI].Split(""\"")[-1].Replace("".ini"",""""))"; If ("$($strTemp)" -like "*@@*") { [System.String]$strTemp = "$($strTemp.Replace(""@@"",""\""))"; } ##### Add The UserName To The UserName Array Write-Verbose "Generated UserName: ""$($strTemp)"""; $hashTemp.Add("$($strTemp)", "$($FilePath[$intI])"); } } } default { [System.String]$strEventMessage = "Unknown ParameterSetName ""$($PSCmdlet.ParameterSetName)""."; Log-SnsEventLogMessageHelper -Message "$($strEventMessage)" -EventLogEntryType "Error" -EventSource "$($EventSource)" -Verbose:$false -Debug:$false; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strEventMessage"; Return; } } #================================================================================== #endregion Generate The FilePath Hash #================================================================================== #================================================================================== #region Generate The Credentials Object #================================================================================== ##### Verify The Input Write-Debug "Verify The Input"; If ($hashTemp.Keys.Count -gt 0) { ##### Process Each SnsCredentialFile Write-Debug "Process Each SnsCredentialFile"; [System.Int32]$intI = 0; For ([System.Int32]$intI = 0; $intI -lt $hashTemp.Keys.Count; $intI++) { ##### Verify The Credential File Existence [System.Management.Automation.PSCredential]$objCredentials = $null; If ( ` (-not -not "$($hashTemp.""$($hashTemp.Keys[$intI])"")") ` -and ` (Test-Path -Path "$($hashTemp.""$($hashTemp.Keys[$intI])"")" -PathType "Leaf" -Verbose:$false -Debug:$false) ` ) { ##### Enumerate The Secure Password Variable Write-Verbose "Import File: ""$($hashTemp.""$($hashTemp.Keys[$intI])"")""."; [System.Security.SecureString]$secStrPassword = $null; [System.Security.SecureString]$secStrPassword = [SnsPsModule.SnsCredentialFile]::Import("$($hashTemp.""$($hashTemp.Keys[$intI])"")") | ConvertTo-SecureString -Verbose:$false -Debug:$false; ##### Initialize The PSCredential Variable Write-Debug "Initialize The PSCredential Variable"; [System.Management.Automation.PSCredential]$objCredentials = $null; [System.Management.Automation.PSCredential]$objCredentials = New-Object -TypeName "System.Management.Automation.PSCredential" ` -ArgumentList @("$($hashTemp.Keys[$intI])", $secStrPassword) -Verbose:$false -Debug:$false; ##### } ##### Verify The Credentials Object Creation Write-Debug "Verify The Credentials Object Creation"; If ("$($objCredentials.UserName)" -ne "$($hashTemp.Keys[$intI])") { [System.String]$strEventMessage = "Failed To Generate The Credential Object"; Log-SnsEventLogMessageHelper -Message "$($strEventMessage)" -EventLogEntryType "Error" -EventSource "$($EventSource)" -Verbose:$false -Debug:$false; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strEventMessage"; Return; } Else { ##### Pass The Output Object To The Pipeline Write-Debug "Pass Output Object To The Pipeline"; $PSCmdlet.WriteObject($objCredentials); } } } #================================================================================== #endregion Generate The Credentials Object #================================================================================== } ##### Override The End Method End { Write-Debug "Override End Method"; Write-Verbose ""; ##### Reset The Variables Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "secStrPassword"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "objCredentials"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strTemp"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "intI"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "hashTemp"; $PSCmdlet.MyInvocation.BoundParameters.Keys | ForEach { Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "$($_)"; } ##### Stop The StopWatch $objCmdStopWatch.Stop(); Write-Verbose "Command Elapsed: ""$($objCmdStopWatch.ElapsedMilliseconds)"" Milliseconds." ; Write-Verbose "End!"; } } ##### New-SnsTemporaryPsDrive ===================================================== Function New-SnsTemporaryPsDrive () { <# .SYNOPSIS This CmdLet Maps A Network Folder To Temporary PsDrive Available In The PowerShell Runspace Only. .DESCRIPTION This CmdLet Maps A Network Folder To Temporary PsDrive Available In The PowerShell Runspace Only. The CmdLet Is Capable To Map The Network Location With Authentication. The CmdLet Have Five Parameter Set Corresponding To Authentication Methods: -- Kerberos Here The CmdLet Use The Windows Integrated Authentication With Kerberos In This Scenario Is Created A Remote Session Within The Security Context Of The Currently Logged On User. Impersonalizing Is Not Possible In This Scenario. -- Interactive In This Parameter Set The CmdLet Opens A Window Where The User Can Specify His Credentials. It Cannot Be Used Whenever The CmdLet Is Executed In As Service Mode. Whenever The Script Calling The CmdLet Is Run As A Service On A Scheduled Task There Is No Real Person To Specify The Credentials. -- FolderPath Here Have To Be Specified The UserName And The Full Absolute UNC Folder Path Where The Encrypted Credential File Resides. -- FilePath Here Have To Be Provided The Full Absolute UNC Path To The Credential File. The CmdLet Will Try To Enumerate The UserName From The FileName. -- Credential Here Have To Be Provided System.Management.Automation.PSCredential Object. Whenever Windows Already Have Session To The Specified Network Location, Will Be Used Kerberos ParameterSet, Because Windows Does Not Allow Connection To A Single Resource With Two Different Security Contexts. In Case The Session Creation Fail The CmdLet Is Capable To Log An Event In The Windows Event Viewer Application Log And Kill The Script Process. This Functionality Is Enabled Automatically When EventSource Parameter Is Provided. Throwing Of Terminating Error Normally Keeps The PowerShell Process Running Which Will Prevent The Next Script Scheduled Instances From Execution. Additionally Any Possible Script Monitoring Could Be Cheated That The Monitored Script Is Still Running Because Its Process Is Running. .PARAMETER NetworkShare Specifies The Full Absolute UNC Path To The Network Location Which Have To Be Mapped To The PSSession As PsDrive. Parameter Set: All Parameter Alias: Folder, Path Parameter Validation: Yes, Using Syntax Validation. .PARAMETER Name Specifies The PsDriveName Name. If Omitted The CmdLet Will Generate It Automatically Using The NetworkShare Parameter. Parameter Set: All Parameter Alias: PsDriveName Parameter Validation: N/A .PARAMETER UserName Specifies The UserName Of The Account To Authenticate Against The Network Location. Parameter Set: FolderPath Parameter Alias: N/A Parameter Validation: N/A .PARAMETER FolderPath Specifies The Full Absolute UNC Folder Path Where The Credential File For The Account Specified In UserName Parameter Resides. Parameter Set: FolderPath Parameter Alias: CredentialFolder Parameter Validation: N/A .PARAMETER FilePath Specifies The Full Absolute UNC Path To The Credential File For The Account Which Will Be Used To Authenticate Against The Resource. Parameter Set: FilePath Parameter Alias: CredentialFile Parameter Validation: N/A .PARAMETER Credential Specifies [System.Management.Automation.PSCredential] Object For The Account Which Will Be Used To Authenticate Against The Resource. Parameter Set: Credential Parameter Alias: N/A Parameter Validation: N/A .PARAMETER Interactive Specifies That The User Have To Be Asked Interactively For Credentials. Parameter Set: Interactive Parameter Alias: N/A Parameter Validation: N/A .PARAMETER AuthenticationKerberos Specifies Whether The Currently Logged On User Security Context Shall Be Used To Authenticate Against The Resource. Parameter Set: Kerberos Parameter Alias: Kerberos Parameter Validation: N/A .PARAMETER Attempts Specifies The Number Of Attempts That Have To Be Made To Map The PsDrive. Parameter Set: All Parameter Alias: N/A Parameter Validation: N/A .PARAMETER EventSource Specifies The Application Log Event Source To Be Used For The Error Event Logging Parameter Set: All Parameter Alias: ScriptName Parameter Validation: N/A .PARAMETER PassThru Specifies To Revert The PsDriveName When The Mapping Is Successful. Parameter Set: All Parameter Alias: N/A Parameter Validation: N/A .INPUTS Pipeline Input For NetworkShare Parameter. And Pipeline By Property Name For NetworkShare, Name, UserName, FolderPath, FilePath, Credential, Interactive And AuthenticationKerberos Parameters. .OUTPUTS [System.String[]] Which The PsDriveName Of The Mapped Drive. .NOTES AUTHOR: Svetoslav Nedyalkov Savov THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER. .EXAMPLE [System.String[]]$arrDrives = New-SnsTemporaryPsDrive -AuthenticationKerberos; .EXAMPLE [System.String[]]$arrDrives = New-SnsTemporaryPsDrive -Interactive; .EXAMPLE [System.String[]]$arrDrives = New-SnsTemporaryPsDrive -UserName "john.smith@contoso.com" -FolderPath "C:\"; .EXAMPLE [System.String[]]$arrDrives = New-SnsTemporaryPsDrive -FilePath "C:\john.smith@contoso.com.ini"; .EXAMPLE [System.String[]]$arrDrives = New-SnsTemporaryPsDrive -Credential $objCredential; .LINK svesavov / SnsPsModule - https://github.com/svesavov/SnsPsModule .LINK Svetoslav Savov on LinkedIn - https://www.linkedin.com/in/svetoslavsavov #> [CmdletBinding(PositionalBinding = $false, DefaultParameterSetName = "Kerberos")] Param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] [Alias("Folder", "Path")] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateScript({("$($_)".EndsWith("\"))})] [ValidateScript({((-not "$($_)".StartsWith("\\")) -or ("$($_.Split(""\"")[2])" -eq "$($_.Split(""\"")[2].Replace("" "", """").Replace(""`t"", """"))"))})] [ValidateScript({((-not "$($_)".StartsWith("\\")) -or ("$($_.Split('\')[2])" -match '\b((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.|\b)){4}\b') -or ("$($_.Split('\')[2])" -match '^[a-zA-Z0-9-]+\.[a-zA-Z.]{2,25}$'))})] [ValidateNotNullOrEmpty()][System.String]$NetworkShare, [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $true)] [Alias("PsDriveName")] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateScript({("$($_)" -eq "$($_.Replace("" "", """").Replace(""`t"", """"))")})] [ValidateScript({((Get-PSDrive -PSProvider "FileSystem" -Verbose:$false -Debug:$false).Name -inotcontains "$($_)")})] [ValidateNotNullOrEmpty()][System.String]$Name, [Parameter(Mandatory = $true, ParameterSetName = "FolderPath", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $true)] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateNotNullOrEmpty()][System.String]$UserName, [Parameter(Mandatory = $true, ParameterSetName = "FolderPath", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $true)] [Alias("CredentialFolder")] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateScript({([System.IO.Directory]::Exists("$($_)"))})] [ValidateNotNullOrEmpty()][System.String]$FolderPath, [Parameter(Mandatory = $true, ParameterSetName = "FilePath", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $true)] [Alias("CredentialFile")] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateScript({([System.IO.File]::Exists("$($_)"))})] [ValidateNotNullOrEmpty()][System.String]$FilePath, [Parameter(Mandatory = $true, ParameterSetName = "Credential", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $true)] [ValidateNotNullOrEmpty()][System.Management.Automation.PSCredential]$Credential, [Parameter(Mandatory = $true, ParameterSetName = "Interactive", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $true)] [Switch]$Interactive = $false, [Parameter(Mandatory = $false, ParameterSetName = "Kerberos", ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $true)] [Alias("Kerberos")] [Switch]$AuthenticationKerberos = $false, [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [ValidateNotNullOrEmpty()][System.Int32]$Attempts = 3, [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [Alias("ScriptName")] [ValidateScript({("$($_)" -eq "$($_.Trim())")})] [ValidateScript({([SnsPsModule.SnsEventLog]::VerifySnsEventSource("$($_)"))})] [AllowNull()][AllowEmptyString()][System.String]$EventSource, [Parameter(Mandatory = $false, ValueFromPipeline = $false, ValueFromPipelineByPropertyName = $false)] [Switch]$PassThru ) ##### Override The Begin Method Begin { Write-Debug "Override Begin Method"; Write-Verbose ""; Write-Verbose "New-SnsTemporaryPsDrive"; Write-Verbose "ParameterSetName: $($PSCmdlet.ParameterSetName)`r`n"; ##### Initialize New Measure Watch [System.Diagnostics.Stopwatch]$objCmdStopWatch = [System.Diagnostics.Stopwatch]::StartNew(); If ("$((Get-FileHash -Path ""$($PSCommandPath)"" -Algorithm ""SHA256"" -Verbose:$false -Debug:$false).Hash)" -ne "$($global:SnsModuleCfg.ModuleHash)") { Write-Warning "There Is New Version Of SnsPsModule Module Released. Please Restart The PowerShell Session." -WarningAction "Continue"; }; ##### Initialize The Variables [System.String]$strPsDrive = ""; [System.Management.Automation.PSCredential]$objCredential = $null; [System.Int32]$intI = 0; [System.Collections.Specialized.OrderedDictionary]$hshSplat = [Ordered]@{}; } ##### Override The Process Method Process { Write-Debug "Override Process Method"; Write-Verbose ""; ##### Initialize The Variables [System.String]$strPsDrive = ""; [System.Management.Automation.PSCredential]$objCredential = $null; [System.Int32]$intI = 0; [System.Collections.Specialized.OrderedDictionary]$hshSplat = [Ordered]@{}; #================================================================================== #region Check Whether The PsDrive Not Exists Yet #================================================================================== ##### Enumerate The PsDrives With The Specified Root Write-Verbose "Enumerate The PsDrives With The Specified Root"; [System.String]$strPsDrive = ""; [System.String]$strPsDrive = "$(Get-PSDrive -PSProvider ""FileSystem"" -ErrorAction ""SilentlyContinue"" -WarningAction ""SilentlyContinue"" -Verbose:$false -Debug:$false | ` Where-Object {""$($_.Root)"".TrimEnd(""\"") -eq ""$($NetworkShare)"".TrimEnd(""\"")} -Verbose:$false -Debug:$false | ` Select-Object -ExpandProperty ""Name"" -Verbose:$false -Debug:$false)"; ##### ##### Verify The Output Write-Debug "Verify The Output"; If (-not -not "$($strPsDrive)") { [System.String]$strEventMessage = "PsDrive ""$($NetworkShare)"" Is Already Mapped As ""$($strPsDrive)""."; Log-SnsEventLogMessageHelper -Message "$($strEventMessage)" -EventLogEntryType "Information" -EventSource "$($EventSource)" -Verbose:$false -Debug:$false; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strEventMessage"; } #================================================================================== #endregion Check Whether The PsDrive Not Exists Yet #================================================================================== #================================================================================== #region Enumerate The Drive Name #================================================================================== ##### Continue If The PsDrive Not Mapped Yet Write-Debug "Continue If The PsDrive Not Mapped Yet"; If (-not "$($strPsDrive)") { ##### Verify Whether Name Is Not Provided Write-Debug "Verify Whether Name Is Not Provided"; If (-not "$($Name)") { ##### Enumerate The PsDrive Name Write-Debug "Enumerate The PsDrive Name"; [System.String]$Name = ""; [System.String]$Name = "$($NetworkShare.Split(""\"")[-2].Replace("" "", """"))"; Write-Verbose "Enumerated PsDrive Name: ""$($Name)""."; } ##### Verify Whether The PsDriveName Is Not Already In Use Write-Debug "Verify The Target Server Generation"; If ((Get-PSDrive -PSProvider "FileSystem" -Verbose:$false -Debug:$false | Select-Object -ExpandProperty "Name" -Verbose:$false -Debug:$false) -icontains "$($Name)") { [System.String]$strEventMessage = "The PsDrive Name Is Already In Use With Different Root."; Log-SnsEventLogMessageHelper -Message "$($strEventMessage)" -EventLogEntryType "Error" -EventSource "$($EventSource)" -Verbose:$false -Debug:$false; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strEventMessage"; Return; } } #================================================================================== #endregion Enumerate The Drive Name #================================================================================== #================================================================================== #region Initialize The Credentials Object #================================================================================== ##### Continue If The PsDrive Not Mapped Yet Write-Debug "Continue If The PsDrive Not Mapped Yet"; If (-not "$($strPsDrive)") { ###### Verify The Parameter Set Name Write-Debug "Verify The Parameter Set Name"; Switch ("$($PSCmdlet.ParameterSetName)") { "FolderPath" { ##### Generate The Credential Object In FolderPath Parameter Set Write-Verbose "Generate The Credential Object In FolderPath Parameter Set"; [System.Management.Automation.PSCredential]$objCredential = $null; [System.Management.Automation.PSCredential]$objCredential = Import-SnsCredentialFile -UserName "$($UserName)" -FolderPath "$($FolderPath)" -EventSource "$($EventSource)" -Verbose:$false -Debug:$false; Break; } "FilePath" { ##### Generate The Credential Object In FilePath Parameter Set Write-Verbose "Generate The Credential Object In FilePath Parameter Set"; [System.Management.Automation.PSCredential]$objCredential = $null; [System.Management.Automation.PSCredential]$objCredential = Import-SnsCredentialFile -FilePath "$($FilePath)" -EventSource "$($EventSource)" -Verbose:$false -Debug:$false; Break; } "Credential" { ##### Assign The Provided Credential Object In Credential Parameter Set Write-Verbose "Assign The Provided Credential Object In Credential Parameter Set"; [System.Management.Automation.PSCredential]$objCredential = $null; [System.Management.Automation.PSCredential]$objCredential = $Credential; Break; } Default { ##### Do Nothing } } ##### Verify If It Is Interactive Session Kerberos Wont Be Used And There Are No Credentials Write-Debug "Verify If It Is Interactive Session Kerberos Wont Be Used And There Are No Credentials"; If (("$($PSCmdlet.ParameterSetName)" -ne "Kerberos") -and (-not "$($objCredential.UserName)") -and [System.Environment]::UserInteractive) { ##### Loop Interactive Credentials Dialog With The User Write-Debug "Loop Interactive Credentials Dialog With The User"; Do { ##### Ask The User About Credentials Write-Verbose "Ask The User About Credentials"; [System.Management.Automation.PSCredential]$objCredential = $null; [System.Management.Automation.PSCredential]$objCredential = Get-Credential -Verbose:$false -Debug:$false; } While ((-not "$($objCredential.UserName)") -or (-not "$($objCredential.GetNetworkCredential().Password)")) } ##### Verify The Credentials Object Write-Debug "Verify The Credentials Object"; If (("$($PSCmdlet.ParameterSetName)" -ne "Kerberos") -and (-not "$($objCredential.UserName)")) { [System.String]$strEventMessage = "Failed To Enumerate The Credential Object For PsDrive Mapping."; Log-SnsEventLogMessageHelper -Message "$($strEventMessage)" -EventLogEntryType "Error" -EventSource "$($EventSource)" -Verbose:$false -Debug:$false; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strEventMessage"; [System.Management.Automation.PSCredential]$objCredential = $null; Return; } } #================================================================================== #endregion Initialize The Credentials Object #================================================================================== #================================================================================== #region Map The PsDrive #================================================================================== ##### Continue If The PsDrive Not Mapped Yet Write-Debug "Continue If The PsDrive Not Mapped Yet"; If (-not "$($strPsDrive)") { ##### Loop The PsDrive Creation Write-Debug "Loop The PsDrive Creation"; [System.Int32]$intI = 0; Do { ##### Generate The Splatting Hashtable Write-Debug "Generate The Splatting Hashtable"; [System.Collections.Specialized.OrderedDictionary]$hshSplat = [Ordered]@{}; $hshSplat.Add("Name", "$($Name)"); $hshSplat.Add("Root", "$($NetworkShare)".TrimEnd("\")); $hshSplat.Add("Scope", "Global"); $hshSplat.Add("PSProvider", "FileSystem"); $hshSplat.Add("ErrorAction", "Continue"); $hshSplat.Add("WarningAction", "Continue"); $hshSplat.Add("Verbose", $false); $hshSplat.Add("Debug", $false); $hshSplat.Add("WhatIf", $false); $hshSplat.Add("Confirm", $false); ##### Check About Existing Connections ##### If The User Have A Connection To The Share ##### The Windows Will Not Allow Same connection With 2 Different Users Write-Verbose "Check About Existing Connections"; If ( ` ("$($PSCmdlet.ParameterSetName)" -notlike "Kerberos") ` -and ` (-not -not "$($objCredential.UserName)") ` -and ` (-not -not "$($objCredential.GetNetworkCredential().Password)") ` -and ` ((Get-ChildItem -Path "$($NetworkShare)" -Verbose:$false -Debug:$false -ErrorAction "SilentlyContinue").Count -eq 0) ` ) { Write-Verbose "Using $($objCredential.UserName)'s Credential"; $hshSplat.Add("Credential", $objCredential); } ##### Map The File Share To The PSSession Write-Verbose "Map The File Share To The PSSession As ""$($Name)"""; [System.String]$strPsDrive = ""; [System.String]$strPsDrive = New-PSDrive @hshSplat | Select-Object -ExpandProperty "Name" -Verbose:$false -Debug:$false -ErrorAction "SilentlyContinue" -WarningAction "SilentlyContinue"; ##### Process The Loop Variable And TimeOut If (-not "$($strPsDrive)") { Start-Sleep -Seconds 2 -Verbose:$false -Debug:$false; } [System.Int32]$intI = $intI + 1; } While ((-not "$($strPsDrive)") -and ($intI -lt $Attempts)) ##### Verify The File Share Mapping Write-Debug "Verify The File Share Mapping"; If (-not "$($strPsDrive)") { [System.String]$strEventMessage = "Failed Connect To: ""$($NetworkShare)"""; Log-SnsEventLogMessageHelper -Message "$($strEventMessage)" -EventLogEntryType "Error" -EventSource "$($EventSource)" -Verbose:$false -Debug:$false; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strEventMessage"; Return; } } #================================================================================== #endregion Map The PsDrive #================================================================================== #================================================================================== #region Send The Output Object To The Pipeline #================================================================================== ##### Verify Whether The File Share Is Mapped To The PSSession Write-Debug "Verify Whether The File Share Is Mapped To The PSSession"; If ((-not -not "$($strPsDrive)") -and ($PassThru.IsPresent)) { ##### Pass The Output Object To The Pipeline Write-Debug "Pass Output Object To The Pipeline"; $PSCmdlet.WriteObject("$($strPsDrive):\"); } #================================================================================== #endregion Send The Output Object To The Pipeline #================================================================================== } ##### Override The End Method End { Write-Debug "Override End Method"; Write-Verbose ""; ##### Reset The Variables Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "hshSplat"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "intI"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "objCredential"; Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "strPsDrive"; $PSCmdlet.MyInvocation.BoundParameters.Keys | ForEach { Remove-Variable -Force:$true -WhatIf:$false -Confirm:$false -ErrorAction "SilentlyContinue" -Name "$($_)"; } ##### Stop The StopWatch $objCmdStopWatch.Stop(); Write-Verbose "Command Elapsed: ""$($objCmdStopWatch.ElapsedMilliseconds)"" Milliseconds." ; Write-Verbose "End!"; } } #================================================================================== #endregion Commands #================================================================================== |