How to install/import module before call DSC in the same script?

This topic contains 2 replies, has 2 voices, and was last updated by  Banchy 3 months, 1 week ago.

  • Author
    Posts
  • #93345

    Banchy
    Participant

    Hi guys,

    I'm trying to figure out why my script won't execute from the beginning, skip first lines and start execution from DSC configuration part?
    Generally, I have completely fresh Windows Server 2016 without any additional software installed. I want to provision my server with the code below. And I can't manage it. I'm not sure what I'm missing in the scrip. Can somene hel me?
    Thanks!

    ################################################# 
    ### Temporary relax execution policy ### 
    ################################################# 
    
    Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process -Force 
    
    ################################################# 
    ### Install necessary modules ### 
    ################################################# 
    
    #Save-Module -Name xWebAdministration -Path C:\Temp 
    Install-PackageProvider -Name NuGet -RequiredVersion 2.8.5.201 -Force 
    
    Install-Module -Name xWebAdministration -Force 
    Install-Module -Name xWindowsUpdate -Force 
    Install-Module -Name xComputerManagement -Force 
    
    Import-Module -Name xWebAdministration 
    Import-Module -Name xWindowsUpdate 
    Import-Module -Name xComputerManagement 
    
    ################################################# 
    ### VARIABLES ### 
    ################################################# 
    
    [string]$NewComputerName = "TEST-SRV" # Max 15 characters 
    $softwarePath = "\\SHARE\Temp\DSCInstall" 
    
    ################################################# 
    ### Map network drive with necessary software ### 
    ################################################# 
    
    $net = new-object -ComObject WScript.Network 
    $net.MapNetworkDrive("q:", $softwarePath, $false, $user, $pass) 
    
    ################################################# 
    ### DSC Configuration for Web Server ### 
    ################################################# 
    
    
    Configuration IIS 
    { 
    Import-DscResource –ModuleName PSDesiredStateConfiguration 
    Import-DscResource –ModuleName xWindowsUpdate 
    Import-DscResource –ModuleName xWebAdministration 
    
    Node "localhost" 
    { 
    ### CREATE REQUIRED DIRECTORIES ### 
    File InstallLogDirectory 
    { 
    Ensure = "Present" 
    Type = "Directory" 
    DestinationPath = "C:\DSCDeployment" 
    } 
    
    File LogDirectory 
    { 
    Ensure = "Present" 
    Type = "Directory" 
    DestinationPath = "C:\Logfiles" 
    } 
    
    ### WEBSERVER ROLE ### 
    WindowsFeature Web-Server 
    { 
    Name = "Web-Server" 
    Ensure = "Present" 
    } 
    
    foreach($feature in $disableFeatures) 
    { 
    WindowsFeature $feature 
    { 
    Name= $feature 
    Ensure = "Absent" 
    DependsOn = "[WindowsFeature]Web-Server" 
    } 
    } 
    
    foreach($feature in $enableFeatures) 
    { 
    WindowsFeature $feature 
    { 
    Name = $feature 
    Ensure = "Present" 
    DependsOn = "[WindowsFeature]Web-Server" 
    } 
    } 
    
    ### CONFIGURE IIS ### 
    xIISLogging EnableAllLoggingFields 
    { 
    LogPath = "C:\inetpub\logs\LogFiles" 
    LogFormat = "W3C" 
    LogFlags = "Date","Time","ClientIP","UserName","SiteName","ComputerName","ServerIP","Method","UriStem","UriQuery","HttpStatus","Win32Status","BytesSent","BytesRecv","TimeTaken","ServerPort","UserAgent","Cookie","Referer","ProtocolVersion","Host","HttpSubStatus" 
    LogPeriod = "Daily" 
    LoglocalTimeRollover = $true 
    DependsOn = "[WindowsFeature]Web-Server" 
    } 
    
    xIisFeatureDelegation IPSecurity-FeatureDelegation 
    { 
    SectionName = "security/ipSecurity" 
    OverrideMode = "Allow" 
    DependsOn = "[WindowsFeature]Web-Server" 
    } 
    
    Script IPSecurity-SetDefaults 
    { 
    SetScript = 
    { 
    Set-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST" -Filter "system.webServer/security/ipSecurity" -Name "." -Value @{enableProxyMode=$true; allowUnlisted=$false} 
    } 
    TestScript = { return $false } 
    GetScript = { } 
    } 
    
    Script IPSecurity-RemoveAllowedNetworks 
    { 
    SetScript = 
    { 
    Clear-WebConfiguration -PSPath "MACHINE/WEBROOT/APPHOST" -Filter "system.webServer/security/ipSecurity/*" 
    } 
    TestScript = { return $false } 
    GetScript = { } 
    } 
    
    foreach($network in $IPSecurityAllowedNetworks) 
    { 
    Script "IPSecirity-AllowNetwork[$network]" 
    { 
    SetScript = 
    { 
    $value = @{ allowed = "true"; ipAddress = "$Using:network" } 
    Add-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST" -Filter "system.webServer/security/ipSecurity" -Name "." -Value $value 
    } 
    TestScript = { return $false } 
    GetScript = { } 
    } 
    } 
    
    Script RemoveIISHeader-X-Powered-By 
    { 
    DependsOn = "[WindowsFeature]Web-Server" 
    
    SetScript = 
    { 
    Remove-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST" -Filter "system.webServer/httpProtocol/customHeaders" -Name "." -AtElement @{name='X-Powered-By'} 
    } 
    TestScript = 
    { 
    return (Get-WebConfigurationProperty -PSPath 'MACHINE/WEBROOT/APPHOST' -Filter "/system.webServer/httpProtocol/customHeaders/add[@name='X-Powered-By']" -Name ".").Count -eq 0 
    } 
    GetScript = 
    { 
    #Do Nothing 
    } 
    } 
    
    
    Package UrlRewrite 
    { 
    # Install URL Rewrite module for IIS 
    DependsOn = "[WindowsFeature]Web-Server" 
    Ensure = "Present" 
    Name = "IIS URL Rewrite Module 2" 
    Path = "$softwarePath\rewrite_amd64.msi" 
    Arguments = "/quiet" 
    ProductId = "08F0318A-D113-4CF0-993E-50F191D397AD" 
    } 
    
    Script RewriteRules-X-Forwarded-For 
    { 
    DependsOn = "[Package]UrlRewrite" 
    SetScript = 
    { 
    $rulesPath = "/system.webServer/rewrite/globalRules/rule[@name='X-Forwarded-For Rewrite']" 
    
    Add-WebConfigurationProperty -PSPath 'MACHINE/WEBROOT/APPHOST' -filter "system.webServer/rewrite/globalRules" -name "." -value @{name='X-Forwarded-For Rewrite';patternSyntax='Wildcard';stopProcessing='False'} 
    Set-WebConfigurationProperty -PSPath 'MACHINE/WEBROOT/APPHOST' -filter "$rulesPath/match" -Name "url" -Value '*' 
    
    Add-WebConfigurationProperty -PSPath 'MACHINE/WEBROOT/APPHOST' -filter "$rulesPath/serverVariables" -Name "." -Value @{name='REMOTE_ADDR';value='{HTTP_X_FORWARDED_FOR}'} 
    Add-WebConfigurationProperty -PSPath 'MACHINE/WEBROOT/APPHOST' -filter "$rulesPath/serverVariables" -Name "." -Value @{name='REMOTE_HOST';value='{HTTP_X_FORWARDED_FOR}'} 
    
    Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' -filter "$rulesPath/action" -name "type" -value "None" 
    } 
    TestScript = 
    { 
    return (Get-WebConfigurationProperty -PSPath 'MACHINE/WEBROOT/APPHOST' -Filter "/system.webServer/rewrite/globalRules/rule[@name='X-Forwarded-For Rewrite']" -Name ".").Count -eq 1 
    } 
    GetScript = 
    { 
    #Do Nothing 
    } 
    } 
    
    Script ReWriteRules-AllowedServerVariables 
    { 
    #Adds rewrite allowedServerVariables to applicationHost.config 
    DependsOn = "[Package]UrlRewrite" 
    SetScript = { 
    $current = Get-WebConfiguration /system.webServer/rewrite/allowedServerVariables | select -ExpandProperty collection | ?{$_.ElementTagName -eq "add"} | select -ExpandProperty name 
    $expected = @("HTTPS", "HTTP_X_FORWARDED_FOR", "HTTP_X_FORWARDED_PROTO", "REMOTE_ADDR") 
    $missing = $expected | where {$current -notcontains $_} 
    try 
    { 
    Start-WebCommitDelay 
    $missing | %{ Add-WebConfiguration /system.webServer/rewrite/allowedServerVariables -atIndex 0 -value @{name="$_"} -Verbose } 
    Stop-WebCommitDelay -Commit $true 
    } 
    catch [System.Exception] 
    { 
    $_ | Out-String 
    } 
    } 
    TestScript = { 
    $current = Get-WebConfiguration /system.webServer/rewrite/allowedServerVariables | select -ExpandProperty collection | select -ExpandProperty name 
    $expected = @("HTTPS", "HTTP_X_FORWARDED_FOR", "HTTP_X_FORWARDED_PROTO", "REMOTE_ADDR") 
    $result = -not @($expected| where {$current -notcontains $_}| select -first 1).Count 
    return $result 
    } 
    GetScript = { 
    $allowedServerVariables = Get-WebConfiguration /system.webServer/rewrite/allowedServerVariables | select -ExpandProperty collection 
    return $allowedServerVariables 
    } 
    } 
    
    ### ENABLE RDP ### 
    Registry EnableRDP-Step1 
    { 
    Ensure = "Present" 
    Key = "HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Terminal Server" 
    ValueName = "fDenyTSConnections" 
    ValueData = "0" 
    ValueType = "Dword" 
    Force = $true 
    } 
    
    Registry EnableRDP-Step2 
    { 
    Ensure = "Present" 
    Key = "HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp" 
    ValueName = "UserAuthentication" 
    ValueData = "1" 
    ValueType = "Dword" 
    Force = $true 
    } 
    
    Script EnableRDP 
    { 
    SetScript = { 
    Enable-NetFirewallRule -DisplayGroup "Remote Desktop" 
    eventcreate /t INFORMATION /ID 2 /L APPLICATION /SO "DSC-Client" /D "Enabled Remote Desktop access" 
    } 
    TestScript = { 
    if ((Get-NetFirewallRule -Name "RemoteDesktop-UserMode-In-TCP").Enabled -ne "True") { 
    $false 
    } else { 
    $true 
    } 
    } 
    GetScript = { 
    # Do Nothing 
    } 
    } 
    } 
    } 
    
    # Compile the configuration into MOF file, so DSC configuration can be applied to a node 
    IIS -OutputPath C:\DSCDeployment 
    LocalConfiguration -OutputPath C:\DSCDeployment 
    Set-DscLocalConfigurationManager -Path C:\DSCDeployment\ -Verbose 
    
    Start-DscConfiguration -Wait -Path C:\DSCDeployment\ -Force -Verbose 
    ################################################# 
    
    
  • #93354

    gael
    Participant

    Hiya,

    In short, before it even executes the top of the script, PowerShell will parse the Configuration, and will try to resolve the Import-DSCResource keywords (and fail because they're not available).
    So your script above is likely to fail, before doing anything, telling you some DSC resources or modules are missing.

    One trick to work around this is to put the configuration into a second file, and dot source it after your bootstrap (the part where you install modules).
    By doing so, PowerShell will first parse the file, execute it, and will only parse the Configuration when it reached the dot sourcing of the configuration file.

    The first file would look like:

    Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process -Force 
    
    ################################################# 
    ### Install necessary modules ### 
    ################################################# 
    
    #Save-Module -Name xWebAdministration -Path C:\Temp 
    Install-PackageProvider -Name NuGet -RequiredVersion 2.8.5.201 -Force 
    
    Install-Module -Name xWebAdministration -Force 
    Install-Module -Name xWindowsUpdate -Force 
    Install-Module -Name xComputerManagement -Force 
    
    Import-Module -Name xWebAdministration 
    Import-Module -Name xWindowsUpdate 
    Import-Module -Name xComputerManagement 
    
    ################################################# 
    ### VARIABLES ### 
    ################################################# 
    
    [string]$NewComputerName = "TEST-SRV" # Max 15 characters 
    $softwarePath = "\\SHARE\Temp\DSCInstall" 
    
    ################################################# 
    ### Map network drive with necessary software ### 
    ################################################# 
    
    $net = new-object -ComObject WScript.Network 
    $net.MapNetworkDrive("q:", $softwarePath, $false, $user, $pass) 
    
    . ./Configuration_IIS.ps1
    

    ...and the Configuration_IIS.ps1 would have the rest of the file.

  • #93394

    Banchy
    Participant

    Thank you for a detailed explanation. So, there is no way to do this in one script at all?

You must be logged in to reply to this topic.