Framework/Core/SVT/Services/CloudService.ps1
# # CloudServices.ps1 # Set-StrictMode -Version Latest class CloudService: AzSVTBase { hidden [PSCloudService] $ResourceObject; hidden [bool] $hasClassicPermissions = $true; hidden [string] $cloudServiceAPIVersion = "2016-04-01" CloudService([string] $subscriptionId, [SVTResource] $svtResource): Base($subscriptionId, $svtResource) { $this.GetResourceObject(); } hidden [PSObject] GetResourceObject() { if (-not $this.ResourceObject) { $this.UpdateCloudServiceInstance() } return $this.ResourceObject; } [ControlItem[]] ApplyServiceFilters([ControlItem[]] $controls) { if($null -ne $this.ResourceObject -and ( $this.ResourceObject.DeploymentSlots | Measure-Object).Count -gt 0) { # ignoring "VirtualMachine" slot in cloud service as it contains only classic VM roles. $this.ResourceObject.DeploymentSlots = $this.ResourceObject.DeploymentSlots | Where-Object { $_.SlotType -ne "VirtualMachine" } } return $controls; } hidden [void] UpdateCloudServiceInstance() { #step 1: load data from ARM model $this.ResourceObject = [PSCloudService]::new($this.ResourceContext.ResourceName, $this.ResourceContext.ResourceGroupName) $this.ResourceObject.LoadCloudConfiguration(); #step 2: load from classic config model try { $ResourceAppIdURI = [WebRequestHelper]::GetServiceManagementUrl() $ClassicAccessToken = [ContextHelper]::GetAccessToken($ResourceAppIdURI) if($null -ne $ClassicAccessToken) { $header = "Bearer " + $ClassicAccessToken $headers = @{"Authorization"=$header;"Content-Type"="application/json"; "x-ms-version" ="2013-08-01"} $uri = [string]::Format("{0}/{1}/services/hostedservices/{2}?embed-detail=true",$ResourceAppIdURI, $this.SubscriptionContext.SubscriptionId ,$this.ResourceContext.ResourceName) $cloudServiceResponse = Invoke-WebRequest -Method GET -Uri $uri -Headers $headers if($cloudServiceResponse.StatusCode -ge 200 -and $cloudServiceResponse.StatusCode -le 399) { if($null -ne $cloudServiceResponse.Content) { [xml] $cloudService = $cloudServiceResponse.Content $this.ResourceObject.LoadCloudConfigurationFromClassicConfig($cloudServiceResponse.Content); } } } } catch { $this.hasClassicPermissions = $false; } } hidden [ControlResult] CheckCloudServiceHttpCertificateSSLOnInstanceEndpoints([ControlResult] $controlResult) { if($this.hasClassicPermissions) { $isCompliant = $true; $nonCompliantInstanceEndpoints = @(); if($null -ne $this.ResourceObject -and $null -ne $this.ResourceObject.RoleInstances -and ($this.ResourceObject.RoleInstances | Measure-Object).Count -gt 0) { $this.ResourceObject.RoleInstances | Foreach-Object{ $roleInstance = $_; if($null -ne $roleInstance -and ($roleInstance.InstanceEndpoints | Measure-Object).Count -gt 0) { $roleInstance.InstanceEndpoints | ForEach-Object { $instanceEndpoint = $_ if($instanceEndpoint.Protocol -eq "http") { $isCompliant = $false; $nonCompliantInstanceEndpoints += $_ } } } } } if($isCompliant) { $controlResult.VerificationResult = [VerificationResult]::Passed; } else { #$controlResult.AddMessage([VerificationResult]::Failed, "Instance endpoints of cloud service must have http disabled.",$nonCompliantInstanceEndpoints, $true, "InstanceEndpoints"); $controlResult.AddMessage([VerificationResult]::Failed, "Instance endpoints of cloud service must have http disabled.",$nonCompliantInstanceEndpoints); } } else { $controlResult.AddMessage([VerificationResult]::Manual, "Don't have the required permissions to scan the cloud service."); } return $controlResult; } hidden [ControlResult] CheckCloudServiceHttpCertificateSSLOnInputEndpoints([ControlResult] $controlResult) { $InputEndpoints = @(); if($null -ne $this.ResourceObject -and $null -ne $this.ResourceObject.DeploymentSlots) { $this.ResourceObject.DeploymentSlots | ForEach-Object { $DeploymentSlot = $_; if($null -ne $DeploymentSlot -and $null -ne $DeploymentSlot.Roles) { $DeploymentSlot.Roles | ForEach-Object { $Role = $_; if($null -ne $Role.InputEndpoints) { $Role.InputEndpoints | ForEach-Object { $inpEp = $_; $inputEndpoint = "" | Select-Object SlotName, RoleName, PublicIpAddress, PrivatePort, PublicPort, Protocol, VirtualIpName, IsCompliant $inputEndpoint.SlotName = $DeploymentSlot.SlotName; $inputEndpoint.RoleName = $Role.RoleName; $inputEndpoint.PublicIpAddress = $inpEp.PublicIpAddress; $inputEndpoint.PrivatePort = $inpEp.PrivatePort; $inputEndpoint.PublicPort = $inpEp.PublicPort; $inputEndpoint.Protocol = $inpEp.Protocol; $inputEndpoint.VirtualIpName = $inpEp.VirtualIpName; $inputEndpoint.IsCompliant = "False" if($inpEp.protocol -ne "http") { $inputEndpoint.IsCompliant = "True" } $InputEndpoints += $inputEndpoint } } } } } } $nonCompliantEP = ($InputEndpoints | Where-Object { $_.IsCompliant -eq "False"}) if(($nonCompliantEP | Measure-Object).Count -gt 0) { $controlResult.AddMessage([VerificationResult]::Failed, "There are active non SSL enabled input endpoints.", $nonCompliantEP); #$controlResult.SetStateData("Active non SSL enabled input endpoints", $nonCompliantEP); } else { $controlResult.AddMessage([VerificationResult]::Passed, "There are no active non SSL enabled input endpoints.", $InputEndpoints); } return $controlResult; } hidden [ControlResult] CheckCloudServiceInstanceEndpoints([ControlResult] $controlResult) { if($this.hasClassicPermissions) { $instanceEndpoints = @(); if($null -ne $this.ResourceObject -and $null -ne $this.ResourceObject.DeploymentSlots) { $this.ResourceObject.DeploymentSlots | ForEach-Object { $DeploymentSlot = $_; if($null -ne $DeploymentSlot -and $null -ne $DeploymentSlot.Roles) { $DeploymentSlot.Roles | ForEach-Object { $Role = $_; if($null -ne $Role.RoleInstances -and ($Role.RoleInstances | Measure-Object).Count -gt 0) { $Role.RoleInstances | ForEach-Object { $instanceEndpoints += $_ } } } } } } #$controlResult.AddMessage([VerificationResult]::Verify, "Validate the IP Settings configured for the instance endpoints on cloud service.", $instanceEndpoints, $true, "InstanceEndpoints" ); $controlResult.AddMessage([VerificationResult]::Verify, "Validate the IP Settings configured for the instance endpoints on cloud service.", $instanceEndpoints); } else { #Setting this property ensures that this control result wont be considered for the central telemetry. As control doesn't have the required permissions $controlResult.CurrentSessionContext.Permissions.HasRequiredAccess = $false; $controlResult.AddMessage([VerificationResult]::Manual, "You do not have required permissions to check for internal endpoints on this cloud service. This control requires 'Co-Admin' privilege."); $controlResult.AddMessage([MessageData]::new([Constants]::CoAdminElevatePermissionMsg)); } return $controlResult; } hidden [ControlResult] CheckCloudServiceInputEndpoints([ControlResult] $controlResult) { $InputEndpoints = @(); if($null -ne $this.ResourceObject -and $null -ne $this.ResourceObject.DeploymentSlots) { $this.ResourceObject.DeploymentSlots | ForEach-Object { $DeploymentSlot = $_; if($null -ne $DeploymentSlot -and $null -ne $DeploymentSlot.Roles) { $DeploymentSlot.Roles | ForEach-Object { $Role = $_; if($null -ne $Role.InputEndpoints) { $Role.InputEndpoints | ForEach-Object { $InputEndpoints += $_ } } } } } } #$controlResult.AddMessage([VerificationResult]::Verify, "Remove any un-used internal endpoints from your cloud service.", $inputEndpoints, $true, "InputEndpoints" ); $controlResult.AddMessage([VerificationResult]::Verify, "Remove any un-used input endpoints from your cloud service.", $InputEndpoints); return $controlResult; } hidden [ControlResult] CheckCloudServiceRemoteDebuggingStatus([ControlResult] $controlResult) { if($this.hasClassicPermissions) { $isCompliant = $true; $nonCompliantInstanceEndpoints = @(); if($null -ne $this.ResourceObject -and $null -ne $this.ResourceObject.DeploymentSlots) { $this.ResourceObject.DeploymentSlots | ForEach-Object { $DeploymentSlot = $_; if($null -ne $DeploymentSlot -and $null -ne $DeploymentSlot.Roles) { $DeploymentSlot.Roles | ForEach-Object { $Role = $_; if($null -ne $Role.RoleInstances -and ($Role.RoleInstances | Measure-Object).Count -gt 0) { $Role.RoleInstances | ForEach-Object { $roleInstance = $_; if($null -ne $roleInstance -and ($roleInstance.InstanceEndpoints | Measure-Object).Count -gt 0) { $roleInstance.InstanceEndpoints | ForEach-Object { $instanceEndpoint = $_ if($instanceEndpoint.Name -like "Microsoft.WindowsAzure.Plugins.RemoteDebugger*") { $isCompliant = $false; $nonCompliantInstanceEndpoints += $_ } } } } } } } } } if($isCompliant) { $controlResult.VerificationResult = [VerificationResult]::Passed; } else { #$controlResult.AddMessage([VerificationResult]::Failed, "Remote debugging endpoints enabled on cloud service.", $nonCompliantInstanceEndpoints, $true, "InstanceEndpoints"); $controlResult.AddMessage([VerificationResult]::Failed, "Remote debugging endpoints enabled on cloud service.", $nonCompliantInstanceEndpoints); } } else { #Setting this property ensures that this control result wont be considered for the central telemetry. As control doesn't have the required permissions $controlResult.CurrentSessionContext.Permissions.HasRequiredAccess = $false; $controlResult.AddMessage([VerificationResult]::Manual, "You do not have required permissions to check for remote debugging on this cloud service. This control requires 'Co-Admin' privilege."); $controlResult.AddMessage([MessageData]::new([Constants]::CoAdminElevatePermissionMsg)); } return $controlResult; } hidden [ControlResult] CheckCloudServiceAntiMalwareStatus([ControlResult] $controlResult) { if($this.hasClassicPermissions) { $isCompliant = $false; $extensions = @{}; if($null -ne $this.ResourceObject -and $null -ne $this.ResourceObject.DeploymentSlots) { $this.ResourceObject.DeploymentSlots | ForEach-Object { $DeploymentSlot = $_; if($null -ne $DeploymentSlot -and $null -ne $DeploymentSlot.Roles) { $DeploymentSlot.Roles | ForEach-Object { $Role = $_; $extensions.Add($DeploymentSlot.SlotName + "->" + $Role.RoleName,"Disabled"); if(-not [string]::IsNullOrWhiteSpace($Role.Extensions)) { $Role.Extensions | ForEach-Object { $extension = $_; if($null -ne $extension -and $null -ne $extension.ExtensionId) { $extension.ExtensionId | ForEach-Object { $extensionId = $_ if($extensionId.Id -like "*Antimalware*") { $extensions[$DeploymentSlot.SlotName + "->" + $Role.RoleName] = "Enabled" } } } } } } } } } if($extensions.Count -gt 0) { if(($extensions.Values | Where-Object { $_ -eq "Disabled"} | Measure-Object).Count -le 0) { $controlResult.VerificationResult = [VerificationResult]::Passed; } else { $controlResult.AddMessage([VerificationResult]::Failed, "Antimalware extension is not enabled on cloud service.", $extensions); } } else { $controlResult.VerificationResult = [VerificationResult]::Passed; } } else { #Setting this property ensures that this control result wont be considered for the central telemetry. As control doesn't have the required permissions $controlResult.CurrentSessionContext.Permissions.HasRequiredAccess = $false; $controlResult.AddMessage([VerificationResult]::Manual, "You do not have required permissions to check for antimalware extension on this cloud service. This control requires 'Co-Admin' privilege."); $controlResult.AddMessage([MessageData]::new([Constants]::CoAdminElevatePermissionMsg)); } return $controlResult; } hidden [ControlResult] CheckCloudServiceOSPatchStatus([ControlResult] $controlResult) { $OSPatchVersions = @(); $isCompliant = $true; if($null -ne $this.ResourceObject -and $null -ne $this.ResourceObject.DeploymentSlots) { $this.ResourceObject.DeploymentSlots | ForEach-Object { $DeploymentSlot = $_; if(-not $DeploymentSlot.IsOSAutoUpdateTurnedOn) { $isCompliant = $false; } } } if(-not $isCompliant) { $controlResult.AddMessage([VerificationResult]::Failed, "Cloud service is not set up for automatic OS updates."); } else { $controlResult.AddMessage([VerificationResult]::Passed, "Cloud service is enabled with automatic OS updates"); } return $controlResult; } hidden [ControlResult] CheckCloudServiceRemoteDesktopAccess([ControlResult] $controlResult) { if($this.hasClassicPermissions) { $extensions = @{}; if($null -ne $this.ResourceObject -and $null -ne $this.ResourceObject.DeploymentSlots) { $this.ResourceObject.DeploymentSlots | ForEach-Object { $DeploymentSlot = $_; if($null -ne $DeploymentSlot -and $null -ne $DeploymentSlot.Roles) { $DeploymentSlot.Roles | ForEach-Object { $Role = $_; if($null -ne $Role.Extensions) { $Role.Extensions | ForEach-Object { $extension = $_; $extensions.Add($DeploymentSlot.SlotName + "->" + $extension.RoleName,"Disabled"); if($null -ne $extension -and $null -ne $extension.ExtensionId) { $extension.ExtensionId | ForEach-Object { $extensionId = $_ if($extensionId.Id -like "*RDP*") { $extensions[$DeploymentSlot.SlotName + "->" + $extension.RoleName] = "Enabled" } } } } } } } } } if($extensions.Count -gt 0) { if(($extensions.Values | Where-Object { $_ -eq "Enabled"} | Measure-Object).Count -le 0) { $isCompliant = $true; } else { $isCompliant = $false; $controlResult.AddMessage("Remote desktop endpoints are enabled on cloud service.", $extensions); } } else { $isCompliant = $true; } #$b.ServiceConfiguration.Role[0].ConfigurationSettings.Setting.SyncRoot["Microsoft.WindowsAzure.Plugins.RemoteAccess.Enabled"] $nonCompliantInstanceEndpoints = @(); if($isCompliant) { if($null -ne $this.ResourceObject -and ($this.ResourceObject.DeploymentSlots | Measure-Object).Count -gt 0) { $this.ResourceObject.DeploymentSlots | ForEach-Object { $DeploymentSlot = $_; if($null -ne $DeploymentSlot -and ($DeploymentSlot.Roles| Measure-Object).Count -gt 0) { $DeploymentSlot.Roles | ForEach-Object { $Role = $_; if(($Role.RoleInstances | Measure-Object).Count -gt 0) { $Role.RoleInstances | ForEach-Object { $roleInstance = $_; if($null -ne $roleInstance -and ($roleInstance.InstanceEndpoints | Measure-Object).Count -gt 0) { $roleInstance.InstanceEndpoints | ForEach-Object { $instanceEndpoint = $_ if($instanceEndpoint.Name -like "Microsoft.WindowsAzure.Plugins.RemoteForwarder*") { $isCompliant = $false; $nonCompliantInstanceEndpoints += $_ } } } } } } } } } } if($isCompliant) { $controlResult.VerificationResult = [VerificationResult]::Passed; } else { $controlResult.VerificationResult = [VerificationResult]::Failed; $controlResult.AddMessage("Found RemoteForwarder endpoint configured for your Cloud Service. This endpoint has to be removed from the configuration.", $nonCompliantInstanceEndpoints); } } else { #Setting this property ensures that this control result wont be considered for the central telemetry. As control doesn't have the required permissions $controlResult.CurrentSessionContext.Permissions.HasRequiredAccess = $false; $controlResult.AddMessage([VerificationResult]::Manual, "You do not have required permissions to check for Remote Desktop (RDP) access on this cloud service. This control requires 'Co-Admin' privilege."); $controlResult.AddMessage([MessageData]::new([Constants]::CoAdminElevatePermissionMsg)); } return $controlResult; } } |