diff --git a/CHANGELOG.md b/CHANGELOG.md index c6995dc1..648b2037 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,28 +4,26 @@ The format is based on and uses the types of changes according to [Keep a Change and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] -## Added - -- `AzureConnectedMachine` Composite to install and configure the Azure Connected Machine Agent - -## Changed - -- Fixed Typo in AddsDomainController documentation - - ### Added - AddsDomainController: - add UnprotectFromAccidentalDeletion to allow dc promote if an existing AD computer account is protected +- AzureConnectedMachine: + - Composite to install and configure the Azure Connected Machine Agent - DhcpServerAuthorization: - new resource to authorize DHCP server in AD - FailoverCluster: - add Networks support - add installation of required Windows Features - update documentation +- HyperVReplica + - new resource to configure replication of Hyper-V virtual machines +- HyperVState + - new resource to control state parameters of Hyper-V virtual machines ### Changed +- Fixed Typo in AddsDomainController documentation - DHCPServer: - fix EnableSecurityGroups if resource is not running on a domain controller - HyperV: diff --git a/doc/AzureConnectedMachine.adoc b/doc/AzureConnectedMachine.adoc index ec1395d9..bd55880d 100644 --- a/doc/AzureConnectedMachine.adoc +++ b/doc/AzureConnectedMachine.adoc @@ -1,14 +1,14 @@ // CommonTasks YAML Reference: AzureConnectedMachine -// ====================================== +// ================================================= :YmlCategory: AzureConnectedMachine :abstract: {YmlCategory} downloads and installs the Azure Connected Machine Agent und connects it to your Tenant. A proxy for the download is supported. -[#dscyml_AzureConnectedMachine] +[#dscyml_azureconnectedmachine] = DSC Resource '{YmlCategory}' -[[dscyml_AzureConnectedMachine_abstract, {abstract}]] +[[dscyml_azureconnectedmachine_abstract, {abstract}]] {abstract} @@ -50,7 +50,9 @@ | Location | Mandatory | String -| Specifies the Azure Location in which your Tenant is located. https://azure.microsoft.com/global-infrastructure/services/?products=azure-arc[Available Regions] +| Specifies the Azure Location in which your Tenant is located. + +https://azure.microsoft.com/global-infrastructure/services/?products=azure-arc[Available Regions] | | Credential @@ -69,7 +71,7 @@ | | String | If you want to use a different URL, e.g. an internal Server, to download the ACMA msi -| Default https://aka.ms/AzureConnectedMachineAgent[Microsoft Source] +| Default: https://aka.ms/AzureConnectedMachineAgent[Microsoft Source] | DownloadProxy | @@ -81,11 +83,13 @@ | | string | Define where the Setup should be downloaded to -| Default: C:\DSCData\ACMA +| Default: `C:\DSCData\ACMA` |=== -To configure Azure Connected Machine Settings + +To configure Azure Connected Machine Settings: + [cols="1,1,1,2a,1a" options="header"] |=== | Parameter @@ -94,7 +98,7 @@ To configure Azure Connected Machine Settings | Description | Allowed Values -| IsSingelInstance +| IsSingleInstance | | String | Defines if it is a Single Instance or not. Due to the DSC Ressource it has to be the following way @@ -135,8 +139,8 @@ To configure Azure Connected Machine Settings | | boolean | -|- true -- false +| - true + - false |=== diff --git a/doc/HyperVReplica.adoc b/doc/HyperVReplica.adoc new file mode 100644 index 00000000..e892f6fe --- /dev/null +++ b/doc/HyperVReplica.adoc @@ -0,0 +1,298 @@ +// CommonTasks YAML Reference: HyperVReplica +// ========================================= + +:YmlCategory: HyperVReplica + +:abstract: {YmlCategory} contains DSC resources to configure replication of Hyper-V virtual machines. + +[#dscyml_hypervreplica] += DSC Resource '{YmlCategory}' + +[[dscyml_hypervreplica_abstract, {abstract}]] +{abstract} + +[IMPORTANT] +==== +This DSC resource can only be used to configure Hyper-V Replica. + +Start and control of the replication process is currently *not supported*. +==== + + +[cols="1,3a" options="autowidth" caption=] +|=== +| Source | https://github.com/dsccommunity/CommonTasks/tree/main/source/DSCResources/HyperVReplica +| DSC Resource | https://learn.microsoft.com/en-us/powershell/module/psdesiredstateconfiguration[PSDesiredStateConfiguration] +| Documentation | - https://learn.microsoft.com/en-us/powershell/module/hyper-v/set-vmreplicationserver[Set-VMReplicationServer] + - https://learn.microsoft.com/en-us/powershell/module/hyper-v/enable-vmreplication[Enable-VMReplication] + - https://learn.microsoft.com/en-us/powershell/module/hyper-v/set-vmreplication[Set-VMReplication] +|=== + + +.Attributes of category '{YmlCategory}' +[cols="1,1,1,2a,1a" options="header"] +|=== +| Parameter +| Attribute +| DataType +| Description +| Allowed Values + +| AllowedAuthenticationType +| Mandatory +| String +| Specifies which authentication types the Replica server will use. +| - Kerberos + - Certificate + - CertificateAndKerberos + +| CertificateAuthenticationPort +| +| Int32 +| Specifies the port on which the Replica server will receive replication data using certificate-based authentication. + +This parameter can be set only when the value of the `AllowedAuthType` parameter is `Certificate` or `CertificateAndKerberos`. +| + +| CertificateThumbprint +| +| String +| Specifies the certificate to use for mutual authentication of the replication data. + +This parameter is required only when Certificate is specified as the type of authentication. +Specify the thumbprint of a valid computer certificate from the Personal store. + +The certificate must have all of the following properties to be valid: + +- It must not be expired. +- It must include both client and server authentication extensions for extended key usage (EKU), and an associated private key. +- It must terminate at a valid root certificate. +- It must meet the requirements for the subject common name (CN): +- For servers that are not clustered, the subject common name (CN) must be equal to, or subject alternative name (DNS Name) should contain, the FQDN of the host. +- For servers that are clustered, each node must have two certificates - one in which the subject common name (CN) or subject alternative name (DNS Name) is the name of the node, and the other in which subject common name (CN) or subject alternative name (DNS Name) is FQDN of the Hyper-V Replica Broker. +| + +| DefaultStorageLocation +| +| String +| Specifies the default location to store the virtual hard disk files when a Replica virtual machine is created. + +You must specify this parameter when `ReplicationAllowedFromAnyServer` is `True`. +| + +| KerberosAuthenticationPort +| +| Int32 +| Specifies the port that the HTTP listener uses on the Replica server host. +| + +| MonitoringInterval +| +| String +| Specifies how often (the monitoring interval) replication statistics are computed. + +Valid values are: 1 hour, 2 hours, 3 hours, 4 hours, 6 hours, 8 hours, 12 hours, 24 hours, 2 days, 3 days, 4 days, 5 days, 6 days, 7 days. + +Specify in the format days:hours:minutes:seconds, such as 01:00:00 for 1 hour, or 1.00:00:00 for 1 day. +| + +| MonitoringStartTime +| +| String +| Specifies when the monitoring interval starts. +| + +| ReplicationAllowedFromAnyServer +| +| Bool +| Specifies whether to accept replication requests from any server. + +When specified as `True`, `DefaultStorageLocation` must also be specified. +The default storage location and DEFAULT trust group tag are used for virtual machine replicas. +| - True + - *False* (default) + +| [[dscyml_hyperveplica_vmmachines, {YmlCategory}/VMMachines]]<> +| +| Hashtable[] +| Set of virtual machines + +*Only Generation 2 machines are supported!* +| + +|=== + + +[[dscyml_hyperveplica_vmmachines_details]] +.Attributes of category '<>' +[cols="1,1,1,2a,1a" options="header"] +|=== +| Parameter +| Attribute +| DataType +| Description +| Allowed Values + +| Name +| Key +| String +| The desired VM name. +| + +| AuthenticationType +| Mandatory +| String +| Specifies the authentication type to use for virtual machine replication, either Kerberos or Certificate. + +The specified Replica server must support the chosen authentication type. + +*NOTE:* This parameter is used for initial setup of the replication and will not changed on an existing replication configuration. +| - Kerberos + - Certificate + +| AutoResynchronizeEnabled +| +| Boolean +| Enables replicating virtual machines that require resynchronization to be resynchronized automatically. + +(For example, a virtual machine requires resynchronization if the primary server shuts down abruptly). +Resynchronization requires significant storage and processing resources. +We recommended scheduling resynchronization during off-peak hours to reduce the impact to the host and other virtual machines running on the host. +Use the `AutoResynchronizeIntervalStart` and `AutoResynchronizeIntervalEnd` parameters to specify an off-peak time to start the automatic resynchronization. + +| AutoResynchronizeIntervalStart +| +| String +| Specifies the start of the time period in which you want resynchronization to start automatically. +| + +| AutoResynchronizeIntervalEnd +| +| String +| Specifies the end of the time period in which you want resynchronization to start automatically. +| + +| BypassProxyServer +| +| Boolean +| Specifies whether to bypass a proxy server while replicating data to the Replica server. +| - True + - False + +| CompressionEnabled +| +| Boolean +| Specifies whether to compress replication data for this virtual machine when it is sent over the network. +| - True + - False + +| EnableWriteOrderPreservationAcrossDisks +| +| Boolean +| Determines whether all virtual hard disks selected for replication are replicated to the same point in time. + +This is useful if the virtual machine runs an application that saves data across virtual hard disks (for example, one virtual hard disk dedicated for application data, and another virtual hard disk dedicated for application log files). +| - True + - False + +| RecoveryHistory +| +| Int32 +| Specifies whether to store additional recovery points on the replica virtual machine. + +Storing more than the most recent recovery point of the primary virtual machine allows you to recover to an earlier point in time. +However, storing additional recovery points requires more storage and processing resources. +You can configure as many as 24 recovery points to be stored. +| + +| ReplicateHostKvpItems +| +| Boolean +| Specifies whether to replicate host-only key value pairs (KVP) for this virtual machine. +| - True + - False + +| ReplicationFrequencySec +| +| Int32 +| Specifies the frequency, in seconds, at which Hyper-V replicates changes to the Replica server. +| + +| ReplicaServerName +| Mandatory +| String +| Specifies the name of the Replica server to which this virtual machine will be replicated. + +*NOTE:* This parameter is used for initial setup of the replication and will not changed on an existing replication configuration. +| + +| ReplicaServerPort +| Mandatory +| Int32 +| Specifies the port on the Replica server to use for replication traffic. + +Make sure you specify a port that is configured on the Replica server to support the same authentication type you specify using the `AuthenticationType` parameter. + +*NOTE:* This parameter is used for initial setup of the replication and will not changed on an existing replication configuration. +| + +| VSSSnapshotFrequencyHour +| +| Int32 +| Specifies the frequency, in hours, at which Volume Shadow Copy Service (VSS) performs a snapshot backup of the virtual machines. + +Specify this parameter only if application-consistent replication is enabled for the virtual machines and the value you set for the RecoveryHistory parameter is not zero. +The cmdlet sets a value of zero for this parameter if application-consistent replication is disabled. +Do not specify this parameter if you are extending replication from the Replica virtual machine. +| + +|=== + + +.Example +[source, yaml] +---- +HyperVReplica: + AllowedAuthenticationType: Certificate + CertificateAuthenticationPort: 8000 + CertificateThumbprint: c81b94933420221a7ac004a90242d8b1d3e5070d + DefaultStorageLocation: D:\VServer + KerberosAuthenticationPort: 8080 + MonitoringInterval: '1.00:00:00' # for 1 day + MonitoringStartTime: '00:00:00' # start at 00:00 + ReplicationAllowedFromAnyServer: True + VMMachines: + - Name: TESTS01 + ReplicaServerName: ReplicaHost2 + ReplicaServerPort: 443 + AuthenticationType: Certificate + - Name: TESTS02 + ReplicaServerName: ReplicaHost3 + ReplicaServerPort: 443 + AuthenticationType: Kerberos + CompressionEnabled: True + ReplicateHostKvpItems: True + BypassProxyServer: False + VSSSnapshotFrequencyHour: 4 + RecoveryHistory: 4 + ReplicationFrequencySec: 180 + AutoResynchronizeEnabled: True + AutoResynchronizeIntervalStart: '01:00:00' # Start at 01:00 am + AutoResynchronizeIntervalEnd: '06:00:00' # Stop at 06:00 am + EnableWriteOrderPreservationAcrossDisks: True +---- + + +.Recommended Lookup Options in `Datum.yml` (Excerpt) +[source, yaml] +---- +lookup_options: + + HyperVReplica: + merge_hash: deep + HyperVReplica\VMMachines: + merge_hash_array: UniqueKeyValTuples + merge_options: + tuple_keys: + - Name +---- diff --git a/doc/HyperVState.adoc b/doc/HyperVState.adoc new file mode 100644 index 00000000..d0c3906b --- /dev/null +++ b/doc/HyperVState.adoc @@ -0,0 +1,128 @@ +// CommonTasks YAML Reference: HyperVState +// ======================================= + +:YmlCategory: HyperVState + +:abstract: {YmlCategory} contains DSC resources to control state parameters of Hyper-V virtual machines. + +[#dscyml_hypervstate] += DSC Resource '{YmlCategory}' + +[[dscyml_hypervstate_abstract, {abstract}]] +{abstract} + +It can be used to switch the default parameters of the <> configuration at the end of the DSC configuration. +So you can create all VM definitions at the beginning, install Windows Updates, make some necessary reboots and safely start these VMs at last step in the DSC configuration after all critical actions are done. + +// reference links as variables for using more than once +:ref_HyperVDsc: https://github.com/dsccommunity/HyperVDsc[HyperVDsc] + + +[cols="1,3a" options="autowidth" caption=] +|=== +| Source | https://github.com/dsccommunity/CommonTasks/tree/main/source/DSCResources/HyperVState +| DSC Resource | https://github.com/dsccommunity/HyperVDsc +| Documentation | {ref_HyperVDsc} +|=== + + +.Attributes of category '{YmlCategory}' +[cols="1,1,1,2a,1a" options="header"] +|=== +| Parameter +| Attribute +| DataType +| Description +| Allowed Values + +| [[dscyml_hypervstate_vmmachines, {YmlCategory}/VMMachines]]<> +| +| Hashtable[] +| Set of virtual machines + +*Only Generation 2 machines are supported!* +| + +|=== + + +[[dscyml_hypervstate_vmmachines_details]] +.Attributes of category '<>' +[cols="1,1,1,2a,1a" options="header"] +|=== +| Parameter +| Attribute +| DataType +| Description +| Allowed Values + +| Name +| Key +| String +| The desired VM name. +| + +| State +| +| String +| State of the VM. +| - Running + - Paused + - Off + +| AutomaticStartAction +| +| String +| Specifies the action the virtual machine is to take upon start. +| - Nothing + - StartIfRunning + - Start + +| AutomaticStartDelay +| +| Int32 +| Specifies the number of seconds by which the virtual machine's start should be delayed. +| + +| AutomaticStopAction +| +| String +| Specifies the action the virtual machine is to take when the virtual machine host shuts down. +| - TurnOff + - Save + - ShutDown + +|=== + + +.Example +[source, yaml] +---- +HyperVState: + VMMachines: + - Name: XXXADC + State: Running + AutomaticStartAction: Start + AutomaticStartDelay: 30 + AutomaticStopAction: Shutdown + + - Name: XXXAPP + State: Running + AutomaticStartDelay: 60 + AutomaticStopAction: Save +---- + + +.Recommended Lookup Options in `Datum.yml` (Excerpt) +[source, yaml] +---- +lookup_options: + + HyperVState: + merge_hash: deep + HyperVState\VMMachines: + merge_hash_array: UniqueKeyValTuples + merge_options: + tuple_keys: + - Name +---- diff --git a/doc/README.adoc b/doc/README.adoc index 25f691be..299571ee 100644 --- a/doc/README.adoc +++ b/doc/README.adoc @@ -74,6 +74,7 @@ ifdef::env-github[] - <> - <> - <> +- <> - <> - <> - <> @@ -129,6 +130,8 @@ ifdef::env-github[] - <> - <> - <> +- <> +- <> - <> - <> - <> @@ -238,6 +241,7 @@ ifndef::env-github[] | <> | <> | <> | <> | <> | <> +| <> | <> | <> | <> | <> | <> | <> | <> @@ -293,6 +297,8 @@ ifndef::env-github[] | <> | <> | <> | <> | <> | <> +| <> | <> +| <> | <> | <> | <> | <> | <> | <> | <> @@ -407,6 +413,8 @@ include::AddsWaitForDomains.adoc[leveloffset=+1] <<<< include::AuditPolicies.adoc[leveloffset=+1] <<<< +include::AzureConnectedMachine.adoc[leveloffset=+1] +<<<< include::Bitlocker.adoc[leveloffset=+1] <<<< include::CertificateAuthorities.adoc[leveloffset=+1] @@ -517,6 +525,10 @@ include::HostsFileEntries.adoc[leveloffset=+1] <<<< include::HyperV.adoc[leveloffset=+1] <<<< +include::HyperVReplica.adoc[leveloffset=+1] +<<<< +include::HyperVState.adoc[leveloffset=+1] +<<<< include::IpConfiguration.adoc[leveloffset=+1] <<<< include::JeaEndpoints.adoc[leveloffset=+1] diff --git a/doc/Wds.adoc b/doc/Wds.adoc index 2b824587..7c93a923 100644 --- a/doc/Wds.adoc +++ b/doc/Wds.adoc @@ -11,6 +11,29 @@ [[dscyml_wds_abstract, {abstract}]] {abstract} +Fully administration of a Windows Deployment Services server requires the following permissions: + +- *Local administrator* of the Windows Deployment Services server. + + This gives you the following rights: + + * File permissions and permissions to the RemoteInstall folder (the management tools interact with the image store using UNC paths). + * Registry hive permissions. + + Many settings for the Windows Deployment Services server are stored in `HKEY_LOCAL_MACHINE\System`, and you need appropriate permissions to these locations to change them. + +- *Domain administrator* of the domain that contains the Windows Deployment Services server. + + This gives you permissions on the Service Control Point (SCP) in Active Directory Domain Services (AD DS) for the Windows Deployment Services server. + Some configuration settings for the server are stored here. + +- *Enterprise administrator* (optional). + + This gives you Dynamic Host Configuration Protocol (DHCP) authorization permissions. + If DHCP authorization is enabled, the Windows Deployment Services server must be authorized in AD DS before it will be allowed to answer incoming client PXE requests. + DHCP authorization is stored in the Configuration container in AD DS. + +Check these links for more details: + +//- https://social.technet.microsoft.com/Forums/Lync/en-US/16ee1d84-2a3c-4188-a80e-167f15b6de64/minimum-permissions-to-complete-wds-installation?forum=winserversetup[Minimum permissions to complete WDS installation] +- http://systemscenter.ru/wds_deploymentguide.en/html/4aca2aae-a9cf-4b5c-afb2-573603cf77b0.htm[Required Permissions] + // reference links as variables for using more than once :ref_WDSDsc: https://github.com/nyanhp/WDSDsc[WDSDsc] @@ -42,7 +65,7 @@ Use of this DSC Resource on a Windows Server Core installation is not supported. ==== This DSC resource requires an updated version (> 0.11.0) of the {ref_WDSDsc} resource. -Until the next release of {ref_WDSDsc} you can patch the version 0.11.0 with outstanding pull requests on GitHub. +Until the next release of {ref_WDSDsc} you can patch the version 0.11.0 with latest commit on `dev` branch on GitHub. ==== diff --git a/source/DSCResources/HyperVReplica/HyperVReplica.psd1 b/source/DSCResources/HyperVReplica/HyperVReplica.psd1 new file mode 100644 index 00000000..909ad60a --- /dev/null +++ b/source/DSCResources/HyperVReplica/HyperVReplica.psd1 @@ -0,0 +1,9 @@ +@{ + RootModule = 'HyperVReplica.schema.psm1' + ModuleVersion = '0.0.1' + GUID = '0ec1207c-9deb-492a-b88b-f60771532068' + Author = 'NA' + CompanyName = 'NA' + Copyright = 'NA' + DscResourcesToExport = @('HyperVReplica') +} diff --git a/source/DSCResources/HyperVReplica/HyperVReplica.schema.psm1 b/source/DSCResources/HyperVReplica/HyperVReplica.schema.psm1 new file mode 100644 index 00000000..8686d4d6 --- /dev/null +++ b/source/DSCResources/HyperVReplica/HyperVReplica.schema.psm1 @@ -0,0 +1,231 @@ +# see https://github.com/dsccommunity/HyperVDsc +configuration HyperVReplica +{ + param + ( + [Parameter()] + [ValidateSet( 'Kerberos', 'Certificate', 'CertificateAndKerberos' )] + [String] + $AllowedAuthenticationType, + + [Parameter()] + [Int32] + $CertificateAuthenticationPort = 0, + + [Parameter()] + [String] + $CertificateThumbprint, + + [Parameter()] + [String] + $DefaultStorageLocation, + + [Parameter()] + [Int32] + $KerberosAuthenticationPort = 0, + + [Parameter()] + [String] + $MonitoringInterval, + + [Parameter()] + [String] + $MonitoringStartTime, + + [Parameter()] + [Bool] + $ReplicationAllowedFromAnyServer = $False, + + [Parameter()] + [Hashtable[]] + $VMMachines + ) + + $curPSModulePath = $env:PSModulePath + + Import-DscResource -ModuleName PSDesiredStateConfiguration + + Script 'HyperVReplicationServer' + { + TestScript = { + [boolean]$status = $false + + $repSrv = Get-VMReplicationServer -ErrorAction SilentlyContinue + + if ( $null -ne $repSrv ) + { + if ( $repSrv.ReplicationEnabled ) + { + $status = $true + + if ( -not [string]::IsNullOrWhiteSpace( $using:AllowedAuthenticationType ) -and $repSrv.AllowedAuthenticationType -ne $using:AllowedAuthenticationType ) + { + Write-Verbose "AllowedAuthenticationType is $($repSrv.AllowedAuthenticationType) -> expected: $using:AllowedAuthenticationType" + $status = $false + } + + if ( $using:CertificateAuthenticationPort -gt 0 -and $using:CertificateAuthenticationPort -ne $repSrv.CertificateAuthenticationPort ) + { + Write-Verbose "CertificateAuthenticationPort is $($repSrv.CertificateAuthenticationPort) -> expected: $using:CertificateAuthenticationPort" + $status = $false + } + + if ( -not [string]::IsNullOrWhiteSpace( $using:CertificateThumbprint ) -and $using:CertificateThumbprint -ne $repSrv.CertificateThumbprint ) + { + Write-Verbose "CertificateThumbprint is $($repSrv.CertificateThumbprint) -> expected: $using:CertificateThumbprint" + $status = $false + } + + if ( -not [string]::IsNullOrWhiteSpace( $using:DefaultStorageLocation ) -and $using:DefaultStorageLocation -ne $repSrv.DefaultStorageLocation ) + { + Write-Verbose "DefaultStorageLocation is $($repSrv.DefaultStorageLocation) -> expected: $using:DefaultStorageLocation" + $status = $false + } + + if ( $using:KerberosAuthenticationPort -gt 0 -and $using:KerberosAuthenticationPort -ne $repSrv.KerberosAuthenticationPort ) + { + Write-Verbose "KerberosAuthenticationPort is $($repSrv.KerberosAuthenticationPort) -> expected: $using:KerberosAuthenticationPort" + $status = $false + } + + if ( -not [string]::IsNullOrWhiteSpace( $using:MonitoringInterval ) -and $using:MonitoringInterval -ne $repSrv.MonitoringInterval ) + { + Write-Verbose "MonitoringInterval is $($repSrv.MonitoringInterval) -> expected: $using:MonitoringInterval" + $status = $false + } + + if ( -not [string]::IsNullOrWhiteSpace( $using:MonitoringStartTime ) -and $using:MonitoringStartTime -ne $repSrv.MonitoringStartTime ) + { + Write-Verbose "MonitoringStartTime is $($repSrv.MonitoringStartTime) -> expected: $using:MonitoringStartTime" + $status = $false + } + + if ( $using:ReplicationAllowedFromAnyServer -ne $repSrv.ReplicationAllowedFromAnyServer ) + { + Write-Verbose "ReplicationAllowedFromAnyServer is $($repSrv.ReplicationAllowedFromAnyServer) -> expected: $using:ReplicationAllowedFromAnyServer" + $status = $false + } + } + else + { + Write-Verbose "VM Replication is not enabled." + } + } + + return $status + } + SetScript = { + $params = @{ + ReplicationEnabled = $True + ReplicationAllowedFromAnyServer = $using:ReplicationAllowedFromAnyServer + } + if ( -not [string]::IsNullOrWhiteSpace( $using:AllowedAuthenticationType ) ) { $params.AllowedAuthenticationType = $using:AllowedAuthenticationType } + if ( $using:CertificateAuthenticationPort -gt 0 ) { $params.CertificateAuthenticationPort = $using:CertificateAuthenticationPort } + if ( -not [string]::IsNullOrWhiteSpace( $using:CertificateThumbprint ) ) { $params.CertificateThumbprint = $using:CertificateThumbprint } + if ( -not [string]::IsNullOrWhiteSpace( $using:DefaultStorageLocation ) ) { $params.DefaultStorageLocation = $using:DefaultStorageLocation } + if ( $using:KerberosAuthenticationPort -gt 0 ) { $params.KerberosAuthenticationPort = $using:KerberosAuthenticationPort } + if ( -not [string]::IsNullOrWhiteSpace( $using:MonitoringInterval ) ) { $params.MonitoringInterval = $using:MonitoringInterval } + if ( -not [string]::IsNullOrWhiteSpace( $using:MonitoringStartTime ) ) { $params.MonitoringStartTime = $using:MonitoringStartTime } + + Write-Verbose "Set-VMReplicationServer with:`n $($s=''; $params.GetEnumerator() | ForEach-Object { $s+="$($_.Name)='$($_.Value)' " }; $s)" + Set-VMReplicationServer @params + } + GetScript = { return ` + @{ + result = 'N/A' + } + } + } + + if ( $null -ne $VMMachines ) + { + foreach ($vmDef in $VMMachines) + { + if ( [string]::IsNullOrWhiteSpace( $vmDef.Name ) -or + [string]::IsNullOrWhiteSpace( $vmDef.ReplicaServerName ) -or + [string]::IsNullOrWhiteSpace( $vmDef.ReplicaServerPort ) -or + [string]::IsNullOrWhiteSpace( $vmDef.AuthenticationType ) ) + { + throw "ERROR: VM '$($vmDef.Name)': Missing mandatory parameters 'Name', 'ReplicaServerName', 'ReplicaServerPort' or 'AuthenticationType'." + } + + $execName = "HyperVReplica_$($vmDef.Name)" -replace '[\s(){}/\\:-]', '_' + + Script $execName + { + TestScript = { + [boolean]$status = $false + + $vmRep = Get-VMReplication -VMName $using:vmDef.Name -ErrorAction SilentlyContinue + + if ( $null -ne $vmRep ) + { + $status = $true + + if ( (-not [string]::IsNullOrWhiteSpace( $using:vmDef.CompressionEnabled ) -and $vmRep.CompressionEnabled -ne $using:vmDef.CompressionEnabled) -or + (-not [string]::IsNullOrWhiteSpace( $using:vmDef.ReplicateHostKvpItems ) -and $vmRep.ReplicateHostKvpItems -ne $using:vmDef.ReplicateHostKvpItems) -or + (-not [string]::IsNullOrWhiteSpace( $using:vmDef.BypassProxyServer ) -and $vmRep.BypassProxyServer -ne $using:vmDef.BypassProxyServer) -or + (-not [string]::IsNullOrWhiteSpace( $using:vmDef.VSSSnapshotFrequencyHour ) -and $vmRep.VSSSnapshotFrequencyHour -ne $using:vmDef.VSSSnapshotFrequencyHour) -or + (-not [string]::IsNullOrWhiteSpace( $using:vmDef.RecoveryHistory ) -and $vmRep.RecoveryHistory -ne $using:vmDef.RecoveryHistory) -or + (-not [string]::IsNullOrWhiteSpace( $using:vmDef.ReplicationFrequencySec ) -and $vmRep.ReplicationFrequencySec -ne $using:vmDef.ReplicationFrequencySec) -or + (-not [string]::IsNullOrWhiteSpace( $using:vmDef.AutoResynchronizeEnabled ) -and $vmRep.AutoResynchronizeEnabled -ne $using:vmDef.AutoResynchronizeEnabled) -or + (-not [string]::IsNullOrWhiteSpace( $using:vmDef.AutoResynchronizeIntervalStart ) -and $vmRep.AutoResynchronizeIntervalStart -ne $using:vmDef.AutoResynchronizeIntervalStart) -or + (-not [string]::IsNullOrWhiteSpace( $using:vmDef.AutoResynchronizeIntervalEnd ) -and $vmRep.AutoResynchronizeIntervalEnd -ne $using:vmDef.AutoResynchronizeIntervalEnd) -or + (-not [string]::IsNullOrWhiteSpace( $using:vmDef.EnableWriteOrderPreservationAcrossDisks ) -and $vmRep.EnableWriteOrderPreservationAcrossDisks -ne $using:vmDef.EnableWriteOrderPreservationAcrossDisks) ) + { + Write-Verbose "Optional replication parameters are diffent." + $status = $false + } + } + else + { + Write-Verbose "Replication of VM '$($using:vmDef.Name)' is not enabled." + } + + return $status + } + SetScript = { + $params = @{ + VMName = $using:vmDef.Name + } + if ( -not [string]::IsNullOrWhiteSpace( $using:vmDef.CompressionEnabled ) ) { $params.CompressionEnabled = $using:vmDef.CompressionEnabled } + if ( -not [string]::IsNullOrWhiteSpace( $using:vmDef.ReplicateHostKvpItems ) ) { $params.ReplicateHostKvpItems = $using:vmDef.ReplicateHostKvpItems } + if ( -not [string]::IsNullOrWhiteSpace( $using:vmDef.BypassProxyServer ) ) { $params.BypassProxyServer = $using:vmDef.BypassProxyServer } + if ( -not [string]::IsNullOrWhiteSpace( $using:vmDef.VSSSnapshotFrequencyHour ) ) { $params.VSSSnapshotFrequencyHour = $using:vmDef.VSSSnapshotFrequencyHour } + if ( -not [string]::IsNullOrWhiteSpace( $using:vmDef.RecoveryHistory ) ) { $params.RecoveryHistory = $using:vmDef.RecoveryHistory } + if ( -not [string]::IsNullOrWhiteSpace( $using:vmDef.ReplicationFrequencySec ) ) { $params.ReplicationFrequencySec = $using:vmDef.ReplicationFrequencySec } + if ( -not [string]::IsNullOrWhiteSpace( $using:vmDef.AutoResynchronizeEnabled ) ) { $params.AutoResynchronizeEnabled = $using:vmDef.AutoResynchronizeEnabled } + if ( -not [string]::IsNullOrWhiteSpace( $using:vmDef.AutoResynchronizeIntervalStart ) ) { $params.AutoResynchronizeIntervalStart = $using:vmDef.AutoResynchronizeIntervalStart } + if ( -not [string]::IsNullOrWhiteSpace( $using:vmDef.AutoResynchronizeIntervalEnd ) ) { $params.AutoResynchronizeIntervalEnd = $using:vmDef.AutoResynchronizeIntervalEnd } + if ( -not [string]::IsNullOrWhiteSpace( $using:vmDef.EnableWriteOrderPreservationAcrossDisks ) ) { $params.EnableWriteOrderPreservationAcrossDisks = $using:vmDef.EnableWriteOrderPreservationAcrossDisks } + + $vmRep = Get-VMReplication -VMName $using:vmDef.Name -ErrorAction SilentlyContinue + + if ( $null -eq $vmRep ) + { + $params.ReplicaServerName = $using:vmDef.ReplicaServerName + $params.ReplicaServerPort = $using:vmDef.ReplicaServerPort + $params.AuthenticationType = $using:vmDef.AuthenticationType + if ( -not [string]::IsNullOrWhiteSpace( $using:CertificateThumbprint ) ) { $params.CertificateThumbprint = $using:CertificateThumbprint } + + Write-Verbose "Enable-VMReplication with:`n $($s=''; $params.GetEnumerator() | ForEach-Object { $s+="$($_.Name)='$($_.Value)' " }; $s)" + Enable-VMReplication @params + } + else + { + Write-Verbose "Set-VMReplication with:`n $($s=''; $params.GetEnumerator() | ForEach-Object { $s+="$($_.Name)='$($_.Value)' " }; $s)" + Set-VMReplication @params + } + } + GetScript = { return ` + @{ + result = 'N/A' + } + } + } + } + } + + # restore PSModulePath to reset changes made during MOF compilation + $env:PSModulePath = $curPSModulePath +} diff --git a/source/DSCResources/HyperVState/HyperVState.psd1 b/source/DSCResources/HyperVState/HyperVState.psd1 new file mode 100644 index 00000000..444ef444 --- /dev/null +++ b/source/DSCResources/HyperVState/HyperVState.psd1 @@ -0,0 +1,9 @@ +@{ + RootModule = 'HyperVState.schema.psm1' + ModuleVersion = '0.0.1' + GUID = '0651c256-795a-4992-a8d9-fb34cac57f41' + Author = 'NA' + CompanyName = 'NA' + Copyright = 'NA' + DscResourcesToExport = @('HyperVState') +} diff --git a/source/DSCResources/HyperVState/HyperVState.schema.psm1 b/source/DSCResources/HyperVState/HyperVState.schema.psm1 new file mode 100644 index 00000000..206575a5 --- /dev/null +++ b/source/DSCResources/HyperVState/HyperVState.schema.psm1 @@ -0,0 +1,98 @@ +# see https://github.com/dsccommunity/HyperVDsc +configuration HyperVState +{ + param + ( + [Parameter(Mandatory = $true)] + [Hashtable[]] + $VMMachines + ) + + $curPSModulePath = $env:PSModulePath + + Import-DscResource -ModuleName PSDesiredStateConfiguration + Import-DscResource -ModuleName HyperVDsc + + + foreach ($vmmachine in $VMMachines) + { + $vmName = $vmmachine.Name + $vmState = $vmmachine.State + $automaticStartAction = $vmmachine.AutomaticStartAction + $automaticStartDelay = $vmmachine.AutomaticStartDelay + $automaticStopAction = $vmmachine.AutomaticStopAction + + $execName = "HyperVState_$vmName" -replace '[\s(){}/\\:-]', '_' + + Script $execName + { + TestScript = { + [boolean]$status = $true + $vmProp = Get-VM -VMName $using:vmName | Select-Object State, AutomaticStartAction, AutomaticStartDelay, AutomaticStopAction + + if ($null -ne $vmProp) + { + Write-Verbose "VM settings of '$using:vmName':`n$vmProp" + + if (($null -ne $using:checkpointType -and $vmProp.State -ne $using:vmState) -or + ($null -ne $using:automaticStartAction -and $vmProp.AutomaticStartAction -ne $using:automaticStartAction) -or + ($null -ne $using:automaticStartDelay -and $vmProp.AutomaticStartDelay -ne $using:automaticStartDelay) -or + ($null -ne $using:automaticStopAction -and $vmProp.AutomaticStopAction -ne $using:automaticStopAction)) + { + $status = $false + } + } + else + { + Write-Verbose 'VM settings not available.' + $status = $false + } + return $status + } + SetScript = { + $vmProps = @{ + VMName = $using:vmName + } + + if ($null -ne $using:automaticStartAction) + { + $vmProps.AutomaticStartAction = $using:automaticStartAction + } + if ($null -ne $using:automaticStartDelay) + { + $vmProps.AutomaticStartDelay = $using:automaticStartDelay + } + if ($null -ne $using:automaticStopAction) + { + $vmProps.AutomaticStopAction = $using:automaticStopAction + } + + Set-VM @vmProps + + if ($null -ne $using:vmState) + { + if ($using:vmState -eq 'Running') + { + Start-VM -Name $using:vmName + } + elseif ($using:vmState -eq 'Off') + { + Stop-VM -Name $using:vmName + } + elseif ($using:vmState -eq 'Paused') + { + Suspend-VM -Name $using:vmName + } + } + } + GetScript = { return ` + @{ + result = 'N/A' + } + } + } + } + + # restore PSModulePath to reset changes made during MOF compilation + $env:PSModulePath = $curPSModulePath +} diff --git a/tests/Unit/DSCResources/Assets/Config/HyperVReplica.yml b/tests/Unit/DSCResources/Assets/Config/HyperVReplica.yml new file mode 100644 index 00000000..865e0f19 --- /dev/null +++ b/tests/Unit/DSCResources/Assets/Config/HyperVReplica.yml @@ -0,0 +1,27 @@ +AllowedAuthenticationType: Certificate +CertificateAuthenticationPort: 8000 +CertificateThumbprint: c81b94933420221a7ac004a90242d8b1d3e5070d +DefaultStorageLocation: D:\VServer +KerberosAuthenticationPort: 8080 +MonitoringInterval: '1.00:00:00' # for 1 day +MonitoringStartTime: '00:00:00' # start at 00:00 +ReplicationAllowedFromAnyServer: True +VMMachines: + - Name: TESTS01 + ReplicaServerName: ReplicaHost2 + ReplicaServerPort: 443 + AuthenticationType: Certificate + - Name: TESTS02 + ReplicaServerName: ReplicaHost3 + ReplicaServerPort: 443 + AuthenticationType: Kerberos + CompressionEnabled: True + ReplicateHostKvpItems: True + BypassProxyServer: False + VSSSnapshotFrequencyHour: 4 + RecoveryHistory: 4 + ReplicationFrequencySec: 180 + AutoResynchronizeEnabled: True + AutoResynchronizeIntervalStart: '01:00:00' # Start at 01:00 am + AutoResynchronizeIntervalEnd: '06:00:00' # Stop at 06:00 am + EnableWriteOrderPreservationAcrossDisks: True diff --git a/tests/Unit/DSCResources/Assets/Config/HyperVState.yml b/tests/Unit/DSCResources/Assets/Config/HyperVState.yml new file mode 100644 index 00000000..f9e5152c --- /dev/null +++ b/tests/Unit/DSCResources/Assets/Config/HyperVState.yml @@ -0,0 +1,16 @@ +VMMachines: + - Name: EUATESTS01 # Name of the virtual machine + State: Running # Running | Paused | Off + AutomaticStartAction: Start + AutomaticStartDelay: 30 + AutomaticStopAction: Save + + - Name: EUATESTS02 # Name of the virtual machine + State: Paused # Running | Paused | Off + AutomaticStopAction: Save + + - Name: EUATESTA01 # Name of the virtual machine + State: Off # Running | Paused | Off + + - Name: EUATESTA02 # Name of the virtual machine + AutomaticStartAction: StartIfRunning