GuestConfiguration.psm1
Set-StrictMode -Version latest $ErrorActionPreference = 'Stop' <# .SYNOPSIS Create a Guest Configuration policy package. .Parameter Name Guest Configuration package name. .Parameter Configuration Compiled DSC configuration document full path. .Parameter DestinationPath Output folder path. It is an optional parameter. if not specified, package will be created in current directory. .Parameter FilesToInclude Path to include additional files/folder with the package. .Example New-GuestConfigurationPackage -Name WindowsTLS -Configuration c:\custom_policy\WindowsTLS\localhost.mof -Out c:\git\repository\release\policy\WindowsTLS #> function New-GuestConfigurationPackage { [CmdletBinding()] param ( [parameter(Position=0, Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $Name, [parameter(Position=1, Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $Configuration, [ValidateNotNullOrEmpty()] [string] $FilesToInclude, [string] $DestinationPath = '.' ) Try { $reservedResourceName = @('OMI_ConfigurationDocument') $unzippedPackagePath = New-Item -ItemType Directory -Force -Path (Join-Path (Join-Path $DestinationPath $Name) 'unzippedPackage') $Configuration = Resolve-Path $Configuration if(-not (Test-Path -Path $Configuration -PathType Leaf)) { Throw "Invalid mof file path, please specify full file path for dsc configuration in -Configuration parameter." } Write-Verbose "Creating Guest Configuration package in temporary directory '$unzippedPackagePath'" # Verify that only supported resources are used in DSC configuration. Test-GuestConfigurationMofResourceDependencies -Path $Configuration # Save DSC configuration to the temporary package path. Save-GuestConfigurationMofDocument -Name $Name -SourcePath $Configuration -DestinationPath (Join-Path $unzippedPackagePath "$Name.mof") # Copy DSC resources Copy-DscResources -MofDocumentPath $Configuration -Destination $unzippedPackagePath # Copy FilesToInclude. $modulePath = Join-Path $unzippedPackagePath 'Modules' $nativeResourcePath = New-Item -ItemType Directory -Force -Path (Join-Path $modulePath 'DscNativeResources') $missingDependencies = @() $resourcesInMofDocument = $resourcesInMofDocument = [Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache]::ImportInstances($Configuration, 4) $resourcesInMofDocument | ForEach-Object { if($_.CimClass.CimClassName -eq 'MSFT_ChefInSpecResource') { if([string]::IsNullOrEmpty($FilesToInclude)) { Throw "Failed to find Chef Inspec profile for '$($_.CimInstanceProperties['Name'].Value)'. Please use FilesToInclude parameter to specify profile path." } $includeFiles = Join-Path $FilesToInclude $_.CimInstanceProperties['Name'].Value if(-not (Test-Path $includeFiles)) { $missingDependencies += $_.CimInstanceProperties['Name'].Value } $chefResourcePath = Join-Path $nativeResourcePath 'MSFT_ChefInSpecResource' Copy-Item $chefResourcePath\install_inspec.sh $modulePath -Force -ErrorAction SilentlyContinue } } if($missingDependencies.Length) { Throw "Failed to find Chef Inspec profile for '$($missingDependencies -join ',')'. Please make sure profile is present on $FilesToInclude path." } else { if(-not [string]::IsNullOrEmpty($FilesToInclude)) { if(Test-Path $FilesToInclude -PathType Leaf) { Copy-Item "$FilesToInclude" $modulePath -Force -ErrorAction SilentlyContinue } else { Copy-Item "$FilesToInclude\*" $modulePath -Recurse -Force -ErrorAction SilentlyContinue } } } # Create Guest Configuration Package. $packagePath = Join-Path $DestinationPath $Name New-Item -ItemType Directory -Force -Path $packagePath | Out-Null $packagePath = Resolve-Path $packagePath $packageFilePath = join-path $packagePath "$Name.zip" Remove-Item $packageFilePath -Force -ErrorAction SilentlyContinue Write-Verbose "Creating Guest Configuration package : $packageFilePath." Add-Type -AssemblyName System.IO.Compression.FileSystem [System.IO.Compression.ZipFile]::CreateFromDirectory($unzippedPackagePath, $packageFilePath) } Finally { } } <# .SYNOPSIS Create Audit, DeployIfNotExists and Initiative policy definitions on specified DestinationPath. .Parameter ContentUri Public http uri of Guest Configuration content package. .Parameter DisplayName Policy display name. .Parameter Description Policy description. .Parameter Parameter Policy parameters. .Parameter Version Policy version. .Parameter Version Destination path. .Parameter Platform Target platform (Windows/Linux) for Guest Configuration policy and content package. Windows is the default platform. .Example New-GuestConfigurationPolicy ` -ContentUri https://github.com/azure/auditservice/release/AuditService.zip ` -DisplayName 'Monitor Windows Service Policy.' ` -Description 'Policy to monitor service on Windows machine.' ` -Version 1.0.0.0 -DestinationPath c:\git\custom_policy #> function New-GuestConfigurationPolicy { [CmdletBinding()] param ( [parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $ContentUri, [parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $DisplayName, [parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $Description, [parameter(Mandatory = $false)] [Hashtable[]] $Parameter, [parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [version] $Version, [parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $DestinationPath, [Parameter()] [ValidateSet('Windows', 'Linux')] [string] $Platform = 'Windows' ) Try { $policyDefinitionsPath = $DestinationPath $unzippedPkgPath = Join-Path $policyDefinitionsPath 'temp' $tempContentPackageFilePath = Join-Path $policyDefinitionsPath 'temp.zip' # update parameter info $ParameterInfo = Update-PolicyParameter -Parameter $Parameter New-Item -ItemType Directory -Force -Path $policyDefinitionsPath | Out-Null # Check if ContentUri is a valid web Uri $uri = $ContentUri -as [System.URI] if(-not ($uri.AbsoluteURI -ne $null -and $uri.Scheme -match '[http|https]')) { Throw "Invalid ContentUri : $ContentUri. Please specify a valid http URI in -ContentUri parameter." } # Generate checksum hash for policy content. Invoke-WebRequest -Uri $ContentUri -OutFile $tempContentPackageFilePath $tempContentPackageFilePath = Resolve-Path $tempContentPackageFilePath $contentHash = (Get-FileHash $tempContentPackageFilePath -Algorithm SHA256).Hash Write-Verbose "SHA256 Hash for content '$ContentUri' : $contentHash." # Get the policy name from policy content. Remove-Item $unzippedPkgPath -Recurse -Force -ErrorAction SilentlyContinue New-Item -ItemType Directory -Force -Path $unzippedPkgPath | Out-Null $unzippedPkgPath = Resolve-Path $unzippedPkgPath Add-Type -AssemblyName System.IO.Compression.FileSystem [System.IO.Compression.ZipFile]::ExtractToDirectory($tempContentPackageFilePath, $unzippedPkgPath) $dscDocument = Get-ChildItem -Path $unzippedPkgPath -Filter *.mof if(-not $dscDocument) { Throw "Invalid policy package, failed to find dsc document in policy package." } $policyName = [System.IO.Path]::GetFileNameWithoutExtension($dscDocument) $DeployPolicyInfo = @{ FileName = "DeployIfNotExists.json" DisplayName = "[Deploy] $DisplayName" Description = $Description ConfigurationName = $policyName ConfigurationVersion = $Version ContentUri = $ContentUri ContentHash = $contentHash ReferenceId = "Deploy_$policyName" ParameterInfo = $ParameterInfo } $AuditPolicyInfo = @{ FileName = "Audit.json" DisplayName = "[Audit] $DisplayName" Description = $Description ConfigurationName = $policyName ReferenceId = "Audit_$policyName" } $InitiativeInfo = @{ FileName = "Initiative.json" DisplayName = "[Initiative] $DisplayName" Description = $Description } Write-Verbose "Creating policy definitions at $policyDefinitionsPath path." New-CustomGuestConfigPolicy -PolicyFolderPath $policyDefinitionsPath -DeployPolicyInfo $DeployPolicyInfo -AuditPolicyInfo $AuditPolicyInfo -InitiativeInfo $InitiativeInfo -Platform $Platform } Finally { # Remove temporary content package. Remove-Item $tempContentPackageFilePath -Force -ErrorAction SilentlyContinue Remove-Item $unzippedPkgPath -Recurse -Force -ErrorAction SilentlyContinue } } <# .SYNOPSIS Publish Guest Configuration policy in Azure Policy Center. .Parameter Path Guest Configuration policy path. .Example Publish-GuestConfigurationPolicy -Path c:\git\custom_policy #> function Publish-GuestConfigurationPolicy { [CmdletBinding()] param ( [parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $Path ) $rmContext = Get-AzContext Write-Verbose "Publishing Guest Configuration policy using '$($rmContext.Name)' AzContext." # Publish policies $subscriptionId = $rmContext.Subscription.Id foreach ($policy in @("Audit.json", "DeployIfNotExists.json")){ $policyFile = join-path $Path $policy $jsonDefinition = Get-Content $policyFile | ConvertFrom-Json | ForEach-Object {$_} $definitionContent = $jsonDefinition.Properties $newAzureRmPolicyDefinitionParameters = @{ Name = $jsonDefinition.name DisplayName = $($definitionContent.DisplayName | ConvertTo-Json -Depth 20) Description = $($definitionContent.Description | ConvertTo-Json -Depth 20).replace('"','') Policy = $($definitionContent.policyRule | ConvertTo-Json -Depth 20) Metadata = $($definitionContent.Metadata | ConvertTo-Json -Depth 20) ApiVersion = '2018-05-01' Verbose = $true } if ($definitionContent.PSObject.Properties.Name -contains 'parameters') { $newAzureRmPolicyDefinitionParameters['Parameter'] = ConvertTo-Json -InputObject $definitionContent.parameters -Depth 15 } Write-Verbose "Publishing '$($jsonDefinition.properties.displayName)' ..." New-AzPolicyDefinition @newAzureRmPolicyDefinitionParameters } # Process initiative $initiativeFile = join-path $Path "Initiative.json" $jsonDefinition = Get-Content $initiativeFile | ConvertFrom-Json | ForEach-Object {$_} # Update with subscriptionId foreach($definitions in $jsonDefinition.properties.policyDefinitions){ $definitions.policyDefinitionId = "/subscriptions/$subscriptionId" + $definitions.policyDefinitionId } Write-Verbose "Publishing '$($jsonDefinition.properties.displayName)' ..." $initiativeContent = $jsonDefinition.Properties $newAzureRmPolicySetDefinitionParameters = @{ Name = $jsonDefinition.name DisplayName = $($initiativeContent.DisplayName | ConvertTo-Json -Depth 20) Description = $($initiativeContent.Description | ConvertTo-Json -Depth 20).replace('"','') PolicyDefinition = $($initiativeContent.policyDefinitions | ConvertTo-Json -Depth 20) Metadata = $($initiativeContent.Metadata | ConvertTo-Json -Depth 20) ApiVersion = '2018-05-01' Verbose = $true } if ($initiativeContent.PSObject.Properties.Name -contains 'parameters') { $newAzureRmPolicySetDefinitionParameters['Parameter'] = ConvertTo-Json -InputObject $initiativeContent.parameters -Depth 15 } New-AzPolicySetDefinition @newAzureRmPolicySetDefinitionParameters } <# Private functions. #> function Update-PolicyParameter { [CmdletBinding()] param ( [parameter(Mandatory = $false)] [Hashtable[]] $Parameter ) $updatedParameterInfo = @() foreach($parmInfo in $Parameter) { $param = @{} $param['Type'] = 'string' if($parmInfo.Contains('Name')) { $param['ReferenceName'] = $parmInfo.Name } else { Throw "Policy parameter is missing a mandatory property 'Name'. Please make sure that parameter name is specified in Policy parameter." } if($parmInfo.Contains('DisplayName')) { $param['DisplayName'] = $parmInfo.DisplayName } else { Throw "Policy parameter is missing a mandatory property 'DisplayName'. Please make sure that parameter display name is specified in Policy parameter." } if($parmInfo.Contains('Description')) { $param['Description'] = $parmInfo.Description } if(-not $parmInfo.Contains('ResourceType')) { Throw "Policy parameter is missing a mandatory property 'ResourceType'. Please make sure that configuration resource type is specified in Policy parameter." } elseif(-not $parmInfo.Contains('ResourceId')) { Throw "Policy parameter is missing a mandatory property 'ResourceId'. Please make sure that configuration resource Id is specified in Policy parameter." } else { $param['MofResourceReference'] = "[$($parmInfo.ResourceType)]$($parmInfo.ResourceId)" } if($parmInfo.Contains('ResourcePropertyName')) { $param['MofParameterName'] = $parmInfo.ResourcePropertyName } else { Throw "Policy parameter is missing a mandatory property 'ResourcePropertyName'. Please make sure that configuration resource property name is specified in Policy parameter." } if($parmInfo.Contains('DefaultValue')) { $param['DefaultValue'] = $parmInfo.DefaultValue } if($parmInfo.Contains('AllowedValues')) { $param['AllowedValues'] = $parmInfo.AllowedValues } $updatedParameterInfo += $param; } return $updatedParameterInfo } function Test-GuestConfigurationMofResourceDependencies { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [String] $Path ) $resourcesInMofDocument = [Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache]::ImportInstances($Path, 4) $externalResources = @() for ($i = 0; $i -lt $resourcesInMofDocument.Count; $i++) { if($resourcesInMofDocument[$i].CimInstanceProperties.Name -contains 'ModuleName' -and $resourcesInMofDocument[$i].ModuleName -ne 'GuestConfiguration') { if($resourcesInMofDocument[$i].ModuleName -ieq 'PsDesiredStateConfiguration') { Throw "'PsDesiredStateConfiguration' module is not supported by GuestConfiguration. Please use 'PSDscResources' module instead of 'PsDesiredStateConfiguration' module in DSC configuration." } $configurationName = $resourcesInMofDocument[$i].ConfigurationName Write-Warning -Message "The configuration '$configurationName' is using one or more resources outside of GuestConfiguration module. Please make sure these resources works with PowerShell core 6.0." break } } } function Copy-DscResources { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [String] $MofDocumentPath, [Parameter(Mandatory = $true)] [String] $Destination ) $resourcesInMofDocument = [Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache]::ImportInstances($MofDocumentPath, 4) Write-Verbose "Copy DSC resources ..." $modulePath = New-Item -ItemType Directory -Force -Path (Join-Path $Destination 'Modules') $guestConfigModulePath = New-Item -ItemType Directory -Force -Path (Join-Path $modulePath 'GuestConfiguration') $latestModule = (Get-Module GuestConfiguration -ListAvailable)[0] Copy-Item "$($latestModule.ModuleBase)/*" $guestConfigModulePath -Recurse -Force $modulesToCopy = @{} $resourcesInMofDocument | % { # if resource is not a GuestConfiguration module resource. if($_.CimInstanceProperties.Name -contains 'ModuleName' -and $_.CimInstanceProperties.Name -contains 'ModuleVersion') { $modulesToCopy[$_.CimClass.CimClassName] = @{ModuleName = $_.ModuleName; ModuleVersion = $_.ModuleVersion} } } $modulesToCopy.Values | % { $moduleToCopy = Get-Module -FullyQualifiedName @{ModuleName = $_.ModuleName; RequiredVersion = $_.ModuleVersion} -ListAvailable $moduleToCopyPath = New-Item -ItemType Directory -Force -Path (Join-Path $modulePath $_.ModuleName) Copy-Item "$($moduleToCopy.ModuleBase)/*" $moduleToCopyPath -Recurse -Force } # Copy binary resources. $nativeResourcePath = New-Item -ItemType Directory -Force -Path (Join-Path $modulePath 'DscNativeResources') $resources = Get-DscResource -Module GuestConfiguration $resources | ForEach-Object { if($_.ImplementedAs -eq 'Binary') { $binaryResourcePath = Join-Path (Join-Path $latestModule.ModuleBase 'DscResources') $_.ResourceType Copy-Item $binaryResourcePath $nativeResourcePath -Recurse -Force } } } function Get-GuestConfigurationMofContent { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [String] $Name, [Parameter(Mandatory = $true)] [String] $Path ) Write-Verbose "Parsing Configuration document '$Configuration'" $resourcesInMofDocument = [Microsoft.PowerShell.DesiredStateConfiguration.Internal.DscClassCache]::ImportInstances($Path, 4) # Set the profile path for Chef resource $resourcesInMofDocument | ForEach-Object { if($_.CimClass.CimClassName -eq 'MSFT_ChefInSpecResource') { $profilePath = "$Name/Modules/$($_.Name)" $item = $_.CimInstanceProperties.Item('GithubPath') if($item -eq $null) { $item = [Microsoft.Management.Infrastructure.CimProperty]::Create('GithubPath', $profilePath, [Microsoft.Management.Infrastructure.CimFlags]::Property) $_.CimInstanceProperties.Add($item) } else { $item.Value = $profilePath } } } return $resourcesInMofDocument } function Save-GuestConfigurationMofDocument { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [String] $Name, [Parameter(Mandatory = $true)] [String] $SourcePath, [Parameter(Mandatory = $true)] [String] $DestinationPath ) $resourcesInMofDocument = Get-GuestConfigurationMofContent -Name $Name -Path $SourcePath # if mof contains Chef resource if($resourcesInMofDocument.CimSystemProperties.ClassName -contains 'MSFT_ChefInSpecResource') { Write-Verbose "Serialize DSC document to $DestinationPath path ..." $content = "" for($i = 0; $i -lt $resourcesInMofDocument.Count; $i++) { $resourceClassName = $resourcesInMofDocument[$i].CimSystemProperties.ClassName $content += "instance of $resourceClassName" if($resourceClassName -ne 'OMI_ConfigurationDocument') { $content += ' as $' + "$resourceClassName$i" } $content += "`n{`n" $resourcesInMofDocument[$i].CimInstanceProperties | % { $content += " $($_.Name)" if($_.CimType -eq 'StringArray') { $content += " = {""$($_.Value)""}; `n" } else { $content += " = ""$($_.Value)""; `n" } } $content += "};`n" ; } $content | Out-File $DestinationPath } else { Write-Verbose "Copy DSC document to $DestinationPath path ..." Copy-Item $SourcePath $DestinationPath } } function Format-Json { [CmdletBinding()] [OutputType([String])] param ( [Parameter(Mandatory = $true)] [String] $Json ) $indent = 0 $jsonLines = $Json -Split '\n' $formattedLines = @() $previousLine = '' foreach ($line in $jsonLines) { $skipAddingLine = $false if ($line -match '^\s*\}\s*' -or $line -match '^\s*\]\s*') { # This line contains ] or }, decrement the indentation level $indent-- } $formattedLine = (' ' * $indent * 4) + $line.TrimStart().Replace(': ', ': ') if ($line -match '\s*".*"\s*:\s*\[' -or $line -match '\s*".*"\s*:\s*\{' -or $line -match '^\s*\{\s*' -or $line -match '^\s*\[\s*') { # This line contains [ or {, increment the indentation level $indent++ } if ($previousLine.Trim().EndsWith("{")) { if ($formattedLine.Trim() -in @("}", "},")) { $newLine = "$($previousLine.TrimEnd())$($formattedLine.Trim())" #Write-Verbose -Message "FOUND SHORTENED LINE: $newLine" $formattedLines[($formattedLines.Count - 1)] = $newLine $previousLine = $newLine $skipAddingLine = $true } } if ($previousLine.Trim().EndsWith("[")) { if ($formattedLine.Trim() -in @("]", "],")) { $newLine = "$($previousLine.TrimEnd())$($formattedLine.Trim())" #Write-Verbose -Message "FOUND SHORTENED LINE: $newLine" $formattedLines[($formattedLines.Count - 1)] = $newLine $previousLine = $newLine $skipAddingLine = $true } } if (-not $skipAddingLine -and -not [String]::IsNullOrWhiteSpace($formattedLine)) { $previousLine = $formattedLine $formattedLines += $formattedLine } } $formattedJson = $formattedLines -join "`n" return $formattedJson } function New-InGuestDeployPolicy { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [String] $FileName, [Parameter(Mandatory = $true)] [String] $FolderPath, [Parameter(Mandatory = $true)] [String] $DisplayName, [Parameter(Mandatory = $true)] [String] $Description, [Parameter(Mandatory = $true)] [String] $ConfigurationName, [Parameter(Mandatory = $true)] [version] $ConfigurationVersion, [Parameter(Mandatory = $true)] [String] $ContentUri, [Parameter(Mandatory = $true)] [String] $ContentHash, [Parameter(Mandatory = $true)] [String] $ReferenceId, [Parameter()] [Hashtable[]] $ParameterInfo, [Parameter()] [String] $Guid, [Parameter()] [ValidateSet('Windows', 'Linux')] [String] $Platform = 'Windows' ) if (-not [String]::IsNullOrEmpty($Guid)) { $deployPolicyGuid = $Guid } else { $deployPolicyGuid = [Guid]::NewGuid() } $filePath = Join-Path -Path $FolderPath -ChildPath $FileName $deployPolicyContentHashtable = [Ordered]@{ properties = [Ordered]@{ displayName = $DisplayName policyType = 'Custom' mode = 'Indexed' description = $Description metadata = [Ordered]@{ category = 'Guest Configuration' requiredProviders = @( 'Microsoft.GuestConfiguration' ) } } } $policyRuleHashtable = [Ordered]@{ if = [Ordered]@{ allOf = @( [Ordered]@{ field = 'type' equals = 'Microsoft.Compute/virtualMachines' } ) } then = [Ordered]@{ effect = 'deployIfNotExists' details = [Ordered]@{ type = 'Microsoft.GuestConfiguration/guestConfigurationAssignments' name = $ConfigurationName roleDefinitionIds = @('/providers/microsoft.authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c') deployment = [Ordered]@{ properties = [Ordered]@{ mode = 'incremental' parameters = [Ordered]@{ vmName = [Ordered]@{ value = "[field('name')]" } location = [Ordered]@{ value = "[field('location')]" } configurationName = [Ordered]@{ value = $ConfigurationName } contentUri = [Ordered]@{ value = $ContentUri } contentHash = [Ordered]@{ value = $ContentHash } } template = [Ordered]@{ '$schema' = 'https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#' contentVersion = '1.0.0.0' parameters = [Ordered]@{ vmName = [Ordered]@{ type = 'string' } location = [Ordered]@{ type = 'string' } configurationName = [Ordered]@{ type = 'string' } contentUri = [Ordered]@{ type = 'string' } contentHash = [Ordered]@{ type = 'string' } } resources = @() } } } } } } $guestConfigurationAssignmentHashtable = [Ordered]@{ apiVersion = '2018-11-20' type = 'Microsoft.Compute/virtualMachines/providers/guestConfigurationAssignments' name = "[concat(parameters('vmName'), '/Microsoft.GuestConfiguration/', parameters('configurationName'))]" location = "[parameters('location')]" properties = [Ordered]@{ guestConfiguration = [Ordered]@{ name = "[parameters('configurationName')]" contentUri = "[parameters('contentUri')]" contentHash = "[parameters('contentHash')]" version = $ConfigurationVersion.ToString() } } } if ($Platform -ieq 'Windows') { $policyRuleHashtable['if']['allOf'] += @( [Ordered]@{ anyOf = @( [Ordered]@{ field = 'Microsoft.Compute/imagePublisher' in = @( 'MicrosoftDynamicsAX', 'MicrosoftWindowsDesktop', 'MicrosoftVisualStudio', 'incredibuild', 'MicrosoftWindowsServerHPCPack', 'esri' ) }, [Ordered]@{ allOf = @( [Ordered]@{ field = 'Microsoft.Compute/imagePublisher' equals = 'MicrosoftWindowsServer' }, [Ordered]@{ field = 'Microsoft.Compute/imageSKU' notLike = '2008*' } ) }, [Ordered]@{ allOf = @( [Ordered]@{ field = 'Microsoft.Compute/imagePublisher' equals = 'MicrosoftSQLServer' }, [Ordered]@{ field = 'Microsoft.Compute/imageSKU' notEquals = 'SQL2008R2SP3-WS2008R2SP1' } ) }, [Ordered]@{ allOf = @( [Ordered]@{ field = 'Microsoft.Compute/imagePublisher' equals = 'microsoft-dsvm' }, [Ordered]@{ field = 'Microsoft.Compute/imageOffer' equals = 'dsvm-windows' } ) }, [Ordered]@{ allOf = @( [Ordered]@{ field = 'Microsoft.Compute/imagePublisher' equals = 'microsoft-ads' }, [Ordered]@{ field = 'Microsoft.Compute/imageOffer' in = @( 'standard-data-science-vm', 'windows-data-science-vm' ) } ) }, [Ordered]@{ allOf = @( [Ordered]@{ field = 'Microsoft.Compute/imagePublisher' equals = 'batch' }, [Ordered]@{ field = 'Microsoft.Compute/imageOffer' equals = 'rendering-windows2016' } ) }, [Ordered]@{ allOf = @( [Ordered]@{ field = 'Microsoft.Compute/imagePublisher' equals = 'center-for-internet-security-inc' }, [Ordered]@{ field = 'Microsoft.Compute/imageOffer' like = 'cis-windows-server-201*' } ) }, [Ordered]@{ allOf = @( [Ordered]@{ field = 'Microsoft.Compute/imagePublisher' equals = 'pivotal' }, [Ordered]@{ field = 'Microsoft.Compute/imageOffer' like = 'bosh-windows-server*' } ) }, [Ordered]@{ allOf = @( [Ordered]@{ field = 'Microsoft.Compute/imagePublisher' equals = 'cloud-infrastructure-services' }, [Ordered]@{ field = 'Microsoft.Compute/imageOffer' like = 'ad*' } ) } ) } ) $guestConfigurationExtensionHashtable = [Ordered]@{ apiVersion = '2015-05-01-preview' name = "[concat(parameters('vmName'), '/AzurePolicyforWindows')]" type = 'Microsoft.Compute/virtualMachines/extensions' location = "[parameters('location')]" properties = [Ordered]@{ publisher = 'Microsoft.GuestConfiguration' type = 'ConfigurationforWindows' typeHandlerVersion = '1.1' autoUpgradeMinorVersion = $true settings = @{} protectedSettings = @{} } dependsOn = @( "[concat('Microsoft.Compute/virtualMachines/',parameters('vmName'),'/providers/Microsoft.GuestConfiguration/guestConfigurationAssignments/',parameters('configurationName'))]", "[concat('Microsoft.Compute/virtualMachines/',parameters('vmName'),'/extensions','/EnableHashValidation')]" ) } $guestConfigurationEnableCustomPolicyExtensionHashtable = [Ordered]@{ apiVersion = '2018-06-01' name = "[concat(parameters('vmName'),'/EnableHashValidation')]" type = 'Microsoft.Compute/virtualMachines/extensions' location = "[parameters('location')]" properties = [Ordered]@{ publisher = 'Microsoft.Compute' type = 'CustomScriptExtension' typeHandlerVersion = '1.9' settings = @{} protectedSettings = @{ commandToExecute = 'powershell -c \" & {if(!(Test-Path HKLM:/Software/Microsoft/Windows/DSC)){New-Item -Path HKLM:/Software/Microsoft/Windows/DSC -Force; New-ItemProperty -Path HKLM:/Software/Microsoft/Windows/DSC -Name EnableCustomPolicyHashValidation -Value 1 -PropertyType DWORD -Force}else {New-ItemProperty -Path HKLM:/Software/Microsoft/Windows/DSC -Name EnableCustomPolicyHashValidation -Value 1 -PropertyType DWORD -Force}} \" ' } } } } elseif ($Platform -ieq 'Linux') { $policyRuleHashtable['if']['allOf'] += @( [Ordered]@{ anyOf = @( [Ordered]@{ field = 'Microsoft.Compute/imagePublisher' in = @( 'microsoft-aks', 'AzureDatabricks', 'qubole-inc', 'datastax', 'couchbase', 'scalegrid', 'checkpoint', 'paloaltonetworks' ) }, [Ordered]@{ allOf = @( [Ordered]@{ field = 'Microsoft.Compute/imagePublisher' equals = 'OpenLogic' }, [Ordered]@{ field = 'Microsoft.Compute/imageOffer' like = 'CentOS*' }, [Ordered]@{ field = 'Microsoft.Compute/imageSKU' notLike = '6*' } ) }, [Ordered]@{ allOf = @( [Ordered]@{ field = 'Microsoft.Compute/imagePublisher' equals = 'RedHat' }, [Ordered]@{ field = 'Microsoft.Compute/imageOffer' equals = 'RHEL' }, [Ordered]@{ field = 'Microsoft.Compute/imageSKU' notLike = '6*' } ) }, [Ordered]@{ allOf = @( [Ordered]@{ field = 'Microsoft.Compute/imagePublisher' equals = 'RedHat' }, [Ordered]@{ field = 'Microsoft.Compute/imageOffer' equals = 'osa' } ) }, [Ordered]@{ allOf = @( [Ordered]@{ field = 'Microsoft.Compute/imagePublisher' equals = 'credativ' }, [Ordered]@{ field = 'Microsoft.Compute/imageOffer' equals = 'Debian' }, [Ordered]@{ field = 'Microsoft.Compute/imageSKU' notLike = '7*' } ) }, [Ordered]@{ allOf = @( [Ordered]@{ field = 'Microsoft.Compute/imagePublisher' equals = 'Suse' }, [Ordered]@{ field = 'Microsoft.Compute/imageOffer' like = 'SLES*' }, [Ordered]@{ field = 'Microsoft.Compute/imageSKU' notLike = '11*' } ) }, [Ordered]@{ allOf = @( [Ordered]@{ field = 'Microsoft.Compute/imagePublisher' equals = 'Canonical' }, [Ordered]@{ field = 'Microsoft.Compute/imageOffer' equals = 'UbuntuServer' }, [Ordered]@{ field = 'Microsoft.Compute/imageSKU' notLike = '12*' } ) }, [Ordered]@{ allOf = @( [Ordered]@{ field = 'Microsoft.Compute/imagePublisher' equals = 'microsoft-dsvm' }, [Ordered]@{ field = 'Microsoft.Compute/imageOffer' in = @( 'linux-data-science-vm-ubuntu', 'azureml' ) } ) }, [Ordered]@{ allOf = @( [Ordered]@{ field = 'Microsoft.Compute/imagePublisher' equals = 'cloudera' }, [Ordered]@{ field = 'Microsoft.Compute/imageOffer' equals = 'cloudera-centos-os' }, [Ordered]@{ field = 'Microsoft.Compute/imageSKU' notLike = '6*' } ) }, [Ordered]@{ allOf = @( [Ordered]@{ field = 'Microsoft.Compute/imagePublisher' equals = 'cloudera' }, [Ordered]@{ field = 'Microsoft.Compute/imageOffer' equals = 'cloudera-altus-centos-os' } ) }, [Ordered]@{ allOf = @( [Ordered]@{ field = 'Microsoft.Compute/imagePublisher' equals = 'microsoft-ads' }, [Ordered]@{ field = 'Microsoft.Compute/imageOffer' like = 'linux*' } ) } ) } ) $guestConfigurationExtensionHashtable = [Ordered]@{ apiVersion = '2015-05-01-preview' name = "[concat(parameters('vmName'), '/AzurePolicyforLinux')]" type = 'Microsoft.Compute/virtualMachines/extensions' location = "[parameters('location')]" properties = [Ordered]@{ publisher = 'Microsoft.GuestConfiguration' type = 'ConfigurationforLinux' typeHandlerVersion = '1.0' autoUpgradeMinorVersion = $true } dependsOn = @( "[concat('Microsoft.Compute/virtualMachines/',parameters('vmName'),'/providers/Microsoft.GuestConfiguration/guestConfigurationAssignments/',parameters('configurationName'))]" ) } $guestConfigurationEnableCustomPolicyExtensionHashtable = [Ordered]@{ apiVersion = '2015-06-15' name = "[concat(parameters('vmName'),'/EnableHashValidation')]" type = 'Microsoft.Compute/virtualMachines/extensions' location = "[parameters('location')]" properties = [Ordered]@{ publisher = 'Microsoft.Azure.Extensions' type = 'CustomScript' typeHandlerVersion = '2.0' settings = @{} protectedSettings = @{ script = 'IyEvdXNyL2Jpbi9lbnYgcHl0aG9uCmZyb20gX19mdXR1cmVfXyBpbXBvcnQgcHJpbnRfZnVuY3Rpb24KaW1wb3J0IG9zLHN5cwppbXBvcnQganNvbgppbXBvcnQgc3VicHJvY2VzcwoKZGVmIHByaW50X2Vycm9yKCphcmdzLCAqKmt3YXJncyk6CiAgICBwcmludCgqYXJncywgZmlsZT1zeXMuc3RkZXJyLCAqKmt3YXJncykKICAgIApwYXRoID0gJy92YXIvbGliL3dhYWdlbnQnCmd1ZXN0X2NvbmZpZ19kaXJzID0gb3MubGlzdGRpcihwYXRoKQpmb3IgZ3Vlc3RfY29uZmlnX2RpciBpbiBndWVzdF9jb25maWdfZGlyczoKICAgIGZ1bGxfcGF0aCA9IG9zLnBhdGguam9pbihwYXRoLCBndWVzdF9jb25maWdfZGlyKQogICAgaWYgb3MucGF0aC5pc2RpcihmdWxsX3BhdGgpOgogICAgICAgIGlmICdHdWVzdENvbmZpZ3VyYXRpb24uQ29uZmlndXJhdGlvbmZvckxpbnV4JyBpbiBmdWxsX3BhdGg6CiAgICAgICAgICAgIGRzY19mb2xkZXJfZnVsbF9wYXRoID0gb3MucGF0aC5qb2luKGZ1bGxfcGF0aCwgJ0dDQWdlbnQvRFNDJykKICAgICAgICAgICAgaWYob3MucGF0aC5leGlzdHMoZHNjX2ZvbGRlcl9mdWxsX3BhdGgpKToKICAgICAgICAgICAgICAgIGRzY19jb25maWdfZnVsbF9wYXRoID0gb3MucGF0aC5qb2luKGRzY19mb2xkZXJfZnVsbF9wYXRoLCAnZHNjLmNvbmZpZycpCiAgICAgICAgICAgICAgICBkYXRhID0ge30KICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgaWYob3MucGF0aC5leGlzdHMoZHNjX2NvbmZpZ19mdWxsX3BhdGgpKToKICAgICAgICAgICAgICAgICAgICB3aXRoIG9wZW4oZHNjX2NvbmZpZ19mdWxsX3BhdGgsICdyJykgYXMganNvbkZpbGU6CiAgICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSBqc29uLmxvYWQoanNvbkZpbGUpCiAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgZGF0YVsnUG9saWN5J10gPSB7fQogICAgICAgICAgICAgICAgZGF0YVsnUG9saWN5J11bJ0VuYWJsZUhhc2hWYWxpZGF0aW9uJ10gPSBUcnVlCiAgICAgICAgICAgICAgICB3aXRoIG9wZW4oZHNjX2NvbmZpZ19mdWxsX3BhdGgsICd3JykgYXMgb3V0ZmlsZTogIAogICAgICAgICAgICAgICAgICBqc29uLmR1bXAoZGF0YSwgb3V0ZmlsZSkKICAgICAgICAgICAgICAgIHByaW50KCdSZXN0YXJ0aW5nIGRzYyBzZXJ2aWNlIC4uLicpCiAgICAgICAgICAgICAgICBiYXNoQ29tbWFuZCA9ICJzZXJ2aWNlIGRzY2QgcmVzdGFydCIKICAgICAgICAgICAgICAgIHByb2Nlc3MgPSBzdWJwcm9jZXNzLlBvcGVuKGJhc2hDb21tYW5kLnNwbGl0KCksIHN0ZG91dD1zdWJwcm9jZXNzLlBJUEUpCiAgICAgICAgICAgICAgICBvdXRwdXQsIGVycm9yID0gcHJvY2Vzcy5jb21tdW5pY2F0ZSgpCiAgICAgICAgICAgICAgICBwcmludChvdXRwdXQpCiAgICAgICAgICAgICAgICBzeXMuZXhpdCgwKQogICAgICAgICAgICBlbHNlOgogICAgICAgICAgICAgICAgcHJpbnRfZXJyb3Ioc3RyKCdDb3VsZCBub3QgZmluZCAnKSArIGRzY19mb2xkZXJfZnVsbF9wYXRoICsgJyBwYXRoLiBQbGVhc2UgbWFrZSBzdXJlIEd1ZXN0Q29uZmlndXJhdGlvbiBBZ2VudCBpcyBpbnN0YWxsZWQuJykKICAgICAgICAgICAgICAgIHN5cy5leGl0KDEpCnByaW50X2Vycm9yKHN0cignR3Vlc3RDb25maWd1cmF0aW9uIEFnZW50IGlzIG5vdCBpbnN0YWxsZWQuJykpCnN5cy5leGl0KDEp' } } dependsOn = @( "[concat('Microsoft.Compute/virtualMachines/',parameters('vmName'),'/extensions','/AzurePolicyforLinux')]" ) } } else { throw "The specified platform '$Platform' is not currently supported by this script." } # Handle adding parameters if needed if ($null -ne $ParameterInfo -and $ParameterInfo.Count -gt 0) { if (-not $deployPolicyContentHashtable['properties'].Contains('parameters')) { $deployPolicyContentHashtable['properties']['parameters'] = [Ordered]@{} } if (-not $guestConfigurationAssignmentHashtable['properties']['guestConfiguration'].Contains('configurationParameter')) { $guestConfigurationAssignmentHashtable['properties']['guestConfiguration']['configurationParameter'] = @() } foreach ($currentParameterInfo in $ParameterInfo) { $deployPolicyContentHashtable['properties']['parameters'] += [Ordered]@{ $currentParameterInfo.ReferenceName = [Ordered]@{ type = $currentParameterInfo.Type metadata = [Ordered]@{ displayName = $currentParameterInfo.DisplayName } } } if ($currentParameterInfo.ContainsKey('Description')) { $deployPolicyContentHashtable['properties']['parameters'][$currentParameterInfo.ReferenceName]['metadata']['description'] = $currentParameterInfo['Description'] } if ($currentParameterInfo.ContainsKey('DefaultValue')) { $deployPolicyContentHashtable['properties']['parameters'][$currentParameterInfo.ReferenceName] += [Ordered]@{ defaultValue = $currentParameterInfo.DefaultValue } } if ($currentParameterInfo.ContainsKey('AllowedValues')) { $deployPolicyContentHashtable['properties']['parameters'][$currentParameterInfo.ReferenceName] += [Ordered]@{ allowedValues = $currentParameterInfo.AllowedValues } } if ($currentParameterInfo.ContainsKey('DeploymentValue')) { $policyRuleHashtable['then']['details']['deployment']['properties']['parameters'] += [Ordered]@{ $currentParameterInfo.ReferenceName = [Ordered]@{ value = $currentParameterInfo.DeploymentValue } } } else { $policyRuleHashtable['then']['details']['deployment']['properties']['parameters'] += [Ordered]@{ $currentParameterInfo.ReferenceName = [Ordered]@{ value = "[parameters('$($currentParameterInfo.ReferenceName)')]" } } } $policyRuleHashtable['then']['details']['deployment']['properties']['template']['parameters'] += [Ordered]@{ $currentParameterInfo.ReferenceName = [Ordered]@{ type = $currentParameterInfo.Type } } if ($currentParameterInfo.ContainsKey('ConfigurationValue')) { $guestConfigurationAssignmentHashtable['properties']['guestConfiguration']['configurationParameter'] += [Ordered]@{ name = "$($currentParameterInfo.MofResourceReference);$($currentParameterInfo.MofParameterName)" value = $currentParameterInfo.ConfigurationValue } } else { $guestConfigurationAssignmentHashtable['properties']['guestConfiguration']['configurationParameter'] += [Ordered]@{ name = "$($currentParameterInfo.MofResourceReference);$($currentParameterInfo.MofParameterName)" value = "[parameters('$($currentParameterInfo.ReferenceName)')]" } } } } $policyRuleHashtable['then']['details']['deployment']['properties']['template']['resources'] += $guestConfigurationAssignmentHashtable $policyRuleHashtable['then']['details']['deployment']['properties']['template']['resources'] += [Ordered]@{ apiVersion = '2017-03-30' type = 'Microsoft.Compute/virtualMachines' identity = [Ordered]@{ type = 'SystemAssigned' } name = "[parameters('vmName')]" location = "[parameters('location')]" } $policyRuleHashtable['then']['details']['deployment']['properties']['template']['resources'] += $guestConfigurationExtensionHashtable $policyRuleHashtable['then']['details']['deployment']['properties']['template']['resources'] += $guestConfigurationEnableCustomPolicyExtensionHashtable $deployPolicyContentHashtable['properties']['policyRule'] = $policyRuleHashtable $deployPolicyContentHashtable += [Ordered]@{ id = "/providers/Microsoft.Authorization/policyDefinitions/$deployPolicyGuid" name = $deployPolicyGuid } $deployPolicyContent = ConvertTo-Json -InputObject $deployPolicyContentHashtable -Depth 100 | ForEach-Object { [System.Text.RegularExpressions.Regex]::Unescape($_) } $formattedDeployPolicyContent = Format-Json -Json $deployPolicyContent if (Test-Path -Path $filePath) { Write-Error -Message "A file at the policy destination path '$filePath' already exists. Please remove this file or specify a different destination path." } else { $null = New-Item -Path $filePath -ItemType 'File' -Value $formattedDeployPolicyContent } return $deployPolicyGuid } function New-InGuestAuditPolicy { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [String] $FileName, [Parameter(Mandatory = $true)] [String] $FolderPath, [Parameter(Mandatory = $true)] [String] $DisplayName, [Parameter(Mandatory = $true)] [String] $Description, [Parameter(Mandatory = $true)] [String] $ConfigurationName, [Parameter(Mandatory = $true)] [String] $ReferenceId, [Parameter()] [String] $Guid ) if (-not [String]::IsNullOrEmpty($Guid)) { $auditPolicyGuid = $Guid } else { $auditPolicyGuid = [Guid]::NewGuid() } $filePath = Join-Path -Path $FolderPath -ChildPath $FileName $auditPolicyContentHashtable = [Ordered]@{ properties = [Ordered]@{ displayName = $DisplayName policyType = 'Custom' mode = 'All' description = $Description metadata = [Ordered]@{ category = 'Guest Configuration' } policyRule = [Ordered]@{ if = [Ordered]@{ allOf = @( [Ordered]@{ field = 'type' equals = 'Microsoft.GuestConfiguration/guestConfigurationAssignments' }, [Ordered]@{ field = 'name' equals = $configurationName }, [Ordered]@{ field = 'Microsoft.GuestConfiguration/guestConfigurationAssignments/complianceStatus' notEquals = 'Compliant' } ) } then = [Ordered]@{ effect = 'audit' } } } id = "/providers/Microsoft.Authorization/policyDefinitions/$auditPolicyGuid" name = $auditPolicyGuid } $auditPolicyContent = ConvertTo-Json -InputObject $auditPolicyContentHashtable -Depth 100 | ForEach-Object { [System.Text.RegularExpressions.Regex]::Unescape($_) } $formattedAuditPolicyContent = Format-Json -Json $auditPolicyContent if (Test-Path -Path $filePath) { Write-Error -Message "A file at the policy destination path '$filePath' already exists. Please remove this file or specify a different destination path." } else { $null = New-Item -Path $filePath -ItemType 'File' -Value $formattedAuditPolicyContent } return $auditPolicyGuid } function New-InGuestPolicyInitiative { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [String] $FileName, [Parameter(Mandatory = $true)] [String] $FolderPath, [Parameter(Mandatory = $true)] [Hashtable[]] $DeployPolicyInfo, [Parameter(Mandatory = $true)] [Hashtable[]] $AuditPolicyInfo, [Parameter(Mandatory = $true)] [String] $DisplayName, [Parameter(Mandatory = $true)] [String] $Description, [Parameter()] [String] $Guid ) if (-not [String]::IsNullOrEmpty($Guid)) { $initiativeGuid = $Guid } else { $initiativeGuid = [Guid]::NewGuid() } $filePath = Join-Path -Path $FolderPath -ChildPath $FileName $policyDefinitions = @() $initiativeContentHashtable = [Ordered]@{ properties = [Ordered]@{ displayName = $DisplayName policyType = 'Custom' description = $Description metadata = [Ordered]@{ category = 'Guest Configuration' } } } foreach ($currentDeployPolicyInfo in $DeployPolicyInfo) { $deployPolicyContentHash = [Ordered]@{ policyDefinitionId = "/providers/Microsoft.Authorization/policyDefinitions/$($currentDeployPolicyInfo.Guid)" policyDefinitionReferenceId = $currentDeployPolicyInfo.ReferenceId } if ($currentDeployPolicyInfo.ContainsKey('ParameterInfo')) { if (-not $initiativeContentHashtable['properties'].Contains('parameters')) { $initiativeContentHashtable['properties']['parameters'] = [Ordered]@{} } if (-not $deployPolicyContentHash.Contains('parameters')) { $deployPolicyContentHash['parameters'] = [Ordered]@{} } foreach ($currentParameterInfo in $currentDeployPolicyInfo.ParameterInfo) { $initiativeContentHashtable['properties']['parameters'] += [Ordered]@{ $currentParameterInfo.ReferenceName = [Ordered]@{ type = $currentParameterInfo.Type metadata = [Ordered]@{ displayName = $currentParameterInfo.DisplayName } } } if ($currentParameterInfo.ContainsKey('Description')) { $initiativeContentHashtable['properties']['parameters'][$currentParameterInfo.ReferenceName]['metadata']['description'] = $currentParameterInfo['Description'] } if ($currentParameterInfo.ContainsKey('DefaultValue')) { $initiativeContentHashtable['properties']['parameters'][$currentParameterInfo.ReferenceName] += [Ordered]@{ defaultValue = $currentParameterInfo.DefaultValue } } if ($currentParameterInfo.ContainsKey('AllowedValues')) { $initiativeContentHashtable['properties']['parameters'][$currentParameterInfo.ReferenceName] += [Ordered]@{ allowedValues = $currentParameterInfo.AllowedValues } } $deployPolicyContentHash['parameters'] += [Ordered]@{ $currentParameterInfo.ReferenceName = [Ordered]@{ value = "[parameters('$($currentParameterInfo.ReferenceName)')]" } } } } $policyDefinitions += $deployPolicyContentHash } foreach ($currentAuditPolicyInfo in $AuditPolicyInfo) { $auditPolicyContentHash = [Ordered]@{ policyDefinitionId = "/providers/Microsoft.Authorization/policyDefinitions/$($currentAuditPolicyInfo.Guid)" policyDefinitionReferenceId = $currentAuditPolicyInfo.ReferenceId } $policyDefinitions += $auditPolicyContentHash } $initiativeContentHashtable['properties']['policyDefinitions'] = $policyDefinitions $initiativeContentHashtable += [Ordered]@{ id = "/providers/Microsoft.Authorization/policySetDefinitions/$initiativeGuid" name = $initiativeGuid } $initiativeContent = ConvertTo-Json -InputObject $initiativeContentHashtable -Depth 100 | ForEach-Object { [System.Text.RegularExpressions.Regex]::Unescape($_) } $formattedInitiativeContent = Format-Json -Json $initiativeContent if (Test-Path -Path $filePath) { Write-Error -Message "A file at the initiative destination path '$filePath' already exists. Please remove this file or specify a different destination path." } else { $null = New-Item -Path $filePath -ItemType 'File' -Value $formattedInitiativeContent } return $initiativeGuid } function New-InGuestPolicy { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [String] $PolicyFolderPath, [Parameter(Mandatory = $true)] [Hashtable[]] $DeployPolicyInfo, [Parameter(Mandatory = $true)] [Hashtable[]] $AuditPolicyInfo, [Parameter(Mandatory = $true)] [Hashtable] $InitiativeInfo, [Parameter()] [ValidateSet('Windows', 'Linux')] [String] $Platform = 'Windows' ) if (Test-Path -Path $PolicyFolderPath) { $null = Remove-Item -Path $PolicyFolderPath -Force -Recurse -ErrorAction 'SilentlyContinue' } $null = New-Item -Path $PolicyFolderPath -ItemType 'Directory' foreach ($currentDeployPolicyInfo in $DeployPolicyInfo) { $currentDeployPolicyInfo['FolderPath'] = $PolicyFolderPath $deployPolicyGuid = New-InGuestDeployPolicy @currentDeployPolicyInfo -Platform $Platform $currentDeployPolicyInfo['Guid'] = $deployPolicyGuid } foreach ($currentAuditPolicyInfo in $AuditPolicyInfo) { $currentAuditPolicyInfo['FolderPath'] = $PolicyFolderPath $auditPolicyGuid = New-InGuestAuditPolicy @currentAuditPolicyInfo $currentAuditPolicyInfo['Guid'] = $auditPolicyGuid } $InitiativeInfo['FolderPath'] = $PolicyFolderPath $InitiativeInfo['DeployPolicyInfo'] = $DeployPolicyInfo $InitiativeInfo['AuditPolicyInfo'] = $AuditPolicyInfo $initiativeGuid = New-InGuestPolicyInitiative @InitiativeInfo } function New-CustomGuestConfigPolicy { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [String] $PolicyFolderPath, [Parameter(Mandatory = $true)] [Hashtable] $DeployPolicyInfo, [Parameter(Mandatory = $true)] [Hashtable] $AuditPolicyInfo, [Parameter(Mandatory = $true)] [Hashtable] $InitiativeInfo, [Parameter()] [ValidateSet('Windows', 'Linux')] [String] $Platform = 'Windows' ) $existingPolicies = Get-AzPolicyDefinition $existingDeployPolicy = $existingPolicies | Where-Object {($_.Properties.PSObject.Properties.Name -contains 'displayName') -and ($_.Properties.displayName -eq ('"' + $DeployPolicyInfo.DisplayName + '"'))} if ($null -ne $existingDeployPolicy) { Write-Verbose -Message "Found policy with name '$($existingDeployPolicy.Properties.displayName)' and guid '$($existingDeployPolicy.Name)'..." $DeployPolicyInfo['Guid'] = $existingDeployPolicy.Name } $existingAuditPolicy = $existingPolicies | Where-Object {($_.Properties.PSObject.Properties.Name -contains 'displayName') -and ($_.Properties.displayName -eq ('"' + $AuditPolicyInfo.DisplayName + '"'))} if ($null -ne $existingAuditPolicy) { Write-Verbose -Message "Found policy with name '$($existingAuditPolicy.Properties.displayName)' and guid '$($existingAuditPolicy.Name)'..." $AuditPolicyInfo['Guid'] = $existingAuditPolicy.Name } $existingInitiative = Get-AzPolicySetDefinition | Where-Object {($_.Properties.PSObject.Properties.Name -contains 'displayName') -and ($_.Properties.displayName -eq ('"' + $InitiativeInfo.DisplayName + '"'))} if ($null -ne $existingInitiative) { Write-Verbose -Message "Found initiative with name '$($existingInitiative.Properties.displayName)' and guid '$($existingInitiative.Name)'..." $InitiativeInfo['Guid'] = $existingInitiative.Name } New-InGuestPolicy @PSBoundParameters } Export-ModuleMember -Function @('New-GuestConfigurationPackage', 'New-GuestConfigurationPolicy', 'Publish-GuestConfigurationPolicy') |