class EffectiveRouteTableFilter(ValueFilter): """Filters network interfaces by the Effective Route Table :example: This policy will get Network Interfaces that have VirtualNetworkGateway and VNet hops. .. code-block:: yaml policies: - name: virtual-network-gateway-hop resource: azure.networkinterface filters: - type: effective-route-table key: routes.value[?source == 'User'].nextHopType op: difference value: - Internet - None - VirtualAppliance """ schema = type_schema('effective-route-table', rinherit=ValueFilter.schema) schema_alias = False def process(self, resources, event=None): resources, _ = ThreadHelper.execute_in_parallel( resources=resources, event=event, execution_method=self._process_resource_set, executor_factory=self.executor_factory, log=log, max_workers=max_workers, chunk_size=chunk_size) return resources def _process_resource_set(self, resources, event): client = self.manager.get_client() matched = [] for resource in resources: try: if 'routes' not in resource: route_table = ( client.network_interfaces.get_effective_route_table( resource['resourceGroup'], resource['name']).result()) resource['routes'] = route_table.serialize() filtered_effective_route_table = super(EffectiveRouteTableFilter, self)\ .process([resource], event) if filtered_effective_route_table: matched.append(resource) except Exception as error: log.warning(error) return matched
class VmRestartAction(BaseAction): schema = type_schema('restart') def process(self, vms): client = self.manager.get_client() for vm in vms: client.virtual_machines.restart(vm['resourceGroup'], vm['name'])
class VmStopAction(BaseAction): schema = type_schema('stop') def process(self, vms): client = self.manager.get_client() for vm in vms: client.virtual_machines.deallocate(vm['resourceGroup'], vm['name'])
class VmPowerOffAction(BaseAction): schema = type_schema('poweroff') def process(self, vms): client = self.manager.get_client() for vm in vms: client.virtual_machines.power_off(vm['resourceGroup'], vm['name'])
class Delete(AzureBaseAction): schema = type_schema('delete') def process_resource_set(self, resources): client = self.manager.get_client() for resource in resources: client.event_subscriptions.delete(resource['properties']['topic'], resource['name'])
class VmPowerOffAction(AzureBaseAction): schema = type_schema('poweroff') def _prepare_processing(self,): self.client = self.manager.get_client() def _process_resource(self, resource): self.client.virtual_machines.power_off(resource['resourceGroup'], resource['name'])
class VmStopAction(AzureBaseAction): schema = type_schema('stop') def _prepare_processing(self,): self.client = self.manager.get_client() def _process_resource(self, resource): self.client.virtual_machines.deallocate(resource['resourceGroup'], resource['name'])
class VmRestartAction(AzureBaseAction): schema = type_schema('restart') def _prepare_processing(self,): self.client = self.manager.get_client() def _process_resource(self, resource): self.client.virtual_machines.restart(resource['resourceGroup'], resource['name'])
class Delete(AzureBaseAction): schema = type_schema('delete') def _prepare_processing(self, ): self.client = self.manager.get_client() def _process_resource(self, resource): self.client.event_subscriptions.delete(resource['properties']['topic'], resource['name'])
class InstanceViewFilter(ValueFilter): schema = type_schema('instance-view', rinherit=ValueFilter.schema) def __call__(self, i): if 'instanceView' not in i: client = self.manager.get_client() instance = client.virtual_machines.get(i['resourceGroup'], i['name'], expand='instanceview').instance_view i['instanceView'] = instance.serialize() return super(InstanceViewFilter, self).__call__(i['instanceView'])
class ConfigurationFilter(ValueFilter): schema = type_schema('configuration', rinherit=ValueFilter.schema) def __call__(self, i): if 'c7n:configuration' not in i: client = self.manager.get_client().web_apps instance = (client.get_configuration(i['resourceGroup'], i['name'])) i['c7n:configuration'] = instance.serialize( keep_readonly=True)['properties'] return super(ConfigurationFilter, self).__call__(i['c7n:configuration'])
class VmStartAction(BaseAction): schema = type_schema('start') def __init__(self, data=None, manager=None, log_dir=None): super(VmStartAction, self).__init__(data, manager, log_dir) self.client = self.manager.get_client() def start(self, resource_group, vm_name): self.client.virtual_machines.start(resource_group, vm_name) def process(self, vms): for vm in vms: self.start(vm['resourceGroup'], vm['name'])
class Delete(BaseAction): schema = type_schema('delete') def process(self, resources, event=None): return ThreadHelper.execute_in_parallel( resources=resources, execution_method=self.process_resource_set, executor_factory=self.executor_factory, log=self.log) def process_resource_set(self, resources): client = self.manager.get_client() for resource in resources: client.event_subscriptions.delete(resource['properties']['topic'], resource['name'])
class StorageSetNetworkRulesAction(AzureBaseAction): schema = type_schema( 'set-network-rules', required=['default-action'], **{ 'default-action': {'enum': ['Allow', 'Deny']}, 'bypass': {'type': 'array', 'items': {'enum': ['AzureServices', 'Logging', 'Metrics']}}, 'ip-rules': { 'type': 'array', 'items': {'ip-address-or-range': {'type': 'string'}} }, 'virtual-network-rules': { 'type': 'array', 'items': {'virtual-network-resource-id': {'type': 'string'}} } } ) def _prepare_processing(self,): self.client = self.manager.get_client() def _process_resource(self, resource): rule_set = NetworkRuleSet(default_action=self.data['default-action']) if 'ip-rules' in self.data: rule_set.ip_rules = [ IPRule( ip_address_or_range=r['ip-address-or-range'], action='Allow') # 'Allow' is the only allowed action for r in self.data['ip-rules']] if 'virtual-network-rules' in self.data: rule_set.virtual_network_rules = [ VirtualNetworkRule( virtual_network_resource_id=r['virtual-network-resource-id'], action='Allow') # 'Allow' is the only allowed action for r in self.data['virtual-network-rules']] if len(self.data.get('bypass', [])) > 0: rule_set.bypass = '******'.join(self.data['bypass']) else: rule_set.bypass = '******' self.client.storage_accounts.update( resource['resourceGroup'], resource['name'], StorageAccountUpdateParameters(network_rule_set=rule_set))
class ResourceTypeFilter(Filter): schema = type_schema('resource-type', required=['values'], values={'type': 'array', 'items': {'type': 'string'}}) def __init__(self, data, manager=None): super(ResourceTypeFilter, self).__init__(data, manager) self.allowed_types = [t.lower() for t in self.data['values']] def process(self, resources, event=None): result = [] for r in resources: if r['type'].lower() in self.allowed_types: result.append(r) return result
class VmResizeAction(AzureBaseAction): """Change a VM's size :example: Resize specific VM by name .. code-block:: yaml policies: - name: resize-vm resource: azure.vm filters: - type: value key: name op: eq value_type: normalize value: fake_vm_name actions: - type: resize vmSize: Standard_A2_v2 """ schema = type_schema('resize', required=['vmSize'], **{'vmSize': { 'type': 'string' }}) def __init__(self, data, manager=None): super(VmResizeAction, self).__init__(data, manager) self.vm_size = self.data['vmSize'] def _prepare_processing(self): self.client = self.manager.get_client() def _process_resource(self, resource): hardware_profile = HardwareProfile(vm_size=self.vm_size) self.client.virtual_machines.begin_update( resource['resourceGroup'], resource['name'], VirtualMachineUpdate(hardware_profile=hardware_profile))
class StorageContainerSetPublicAccessAction(AzureBaseAction): """Action that updates the access level setting on Storage Containers. Programmatically, this will be seen by updating the Public Access setting :example: Finds all Blob Storage Containers that are not private and sets them to private .. code-block:: yaml policies: - name: set-non-production-accounts-private resource: azure.storage-container filters: - type: value key: properties.publicAccess op: not-equal value: None actions: - type: set-public-access value: None """ schema = type_schema('set-public-access', required=['value'], **{'value': { 'enum': ['Container', 'Blob', 'None'] }}) schema_alias = True def _prepare_processing(self): self.client = self.manager.get_client() def _process_resource(self, resource): resource_group = ResourceIdParser.get_resource_group(resource['id']) account_name = ResourceIdParser.get_resource_name( resource['c7n:parent-id']) self.client.blob_containers.update(resource_group, account_name, resource['name'], public_access=self.data['value'])
class EffectiveRouteTableFilter(ValueFilter): schema = type_schema('effective-route-table', rinherit=ValueFilter.schema) schema_alias = False def process(self, resources, event=None): resources, _ = ThreadHelper.execute_in_parallel( resources=resources, event=event, execution_method=self._process_resource_set, executor_factory=self.executor_factory, log=log, max_workers=max_workers, chunk_size=chunk_size ) return resources def _process_resource_set(self, resources, event): client = self.manager.get_client() matched = [] for resource in resources: try: if 'routes' not in resource: route_table = ( client.network_interfaces .get_effective_route_table(resource['resourceGroup'], resource['name']) .result() ) resource['routes'] = route_table.serialize() filtered_effective_route_table = super(EffectiveRouteTableFilter, self)\ .process([resource], event) if filtered_effective_route_table: matched.append(resource) except Exception as error: log.warning(error) return matched
class FrontEndIp(RelatedResourceFilter): """Filters load balancers by frontend public ip. :Example: .. code-block:: yaml policies: - name: loadbalancer-with-ipv6-frontend resource: azure.loadbalancer filters: - type: frontend-public-ip key: properties.publicIPAddressVersion op: in value_type: normalize value: "ipv6" """ schema = type_schema('frontend-public-ip', rinherit=ValueFilter.schema) RelatedResource = "c7n_azure.resources.public_ip.PublicIPAddress" RelatedIdsExpression = "properties.frontendIPConfigurations[].properties.publicIPAddress.id"
class RequireSecureTransferAction(AzureBaseAction): """Action that updates the Secure Transfer setting on Storage Accounts. Programmatically, this will be seen by updating the EnableHttpsTrafficOnly setting :example: Turns on Secure transfer required for all storage accounts. This will reject requests that use HTTP to your storage accounts. .. code-block:: yaml policies: - name: require-secure-transfer resource: azure.storage actions: - type: require-secure-transfer value: True """ # Default to true assuming user wants secure connection schema = type_schema('require-secure-transfer', **{ 'value': { 'type': 'boolean', "default": True }, }) def __init__(self, data, manager=None): super(RequireSecureTransferAction, self).__init__(data, manager) def _prepare_processing(self): self.client = self.manager.get_client() def _process_resource(self, resource): self.client.storage_accounts.update( resource['resourceGroup'], resource['name'], StorageAccountUpdateParameters( enable_https_traffic_only=self.data.get('value')))
class StorageSetNetworkRulesAction(AzureBaseAction): """ Set Network Rules Action Updates Azure Storage Firewalls and Virtual Networks settings. By default the firewall rules are replaced with the new values. The ``append`` flag can be used to force merging the new rules with the existing ones on the resource. You may also reference azure public cloud Service Tags by name in place of an IP address. Use ``ServiceTags.`` followed by the ``name`` of any group from https://www.microsoft.com/en-us/download/details.aspx?id=56519. Note that there are firewall rule number limits and that you will likely need to use a regional block to fit within the limit. The limit for storage accounts is 200 rules. .. code-block:: yaml - type: set-firewall-rules bypass-rules: - Logging - Metrics ip-rules: - 11.12.13.0/16 - ServiceTags.AppService.CentralUS :example: Find storage accounts without any firewall rules. Configure default-action to ``Deny`` and then allow: - Azure Logging and Metrics services - Two specific IPs - Two subnets .. code-block:: yaml policies: - name: add-storage-firewall resource: azure.storage filters: - type: value key: properties.networkAcls.ipRules value_type: size op: eq value: 0 actions: - type: set-firewall-rules bypass-rules: - Logging - Metrics ip-rules: - 11.12.13.0/16 - 21.22.23.24 virtual-network-rules: - <subnet_resource_id> - <subnet_resource_id> """ schema = type_schema( 'set-firewall-rules', required=[], **{ 'default-action': {'enum': ['Allow', 'Deny'], "default": 'Deny'}, 'append': {'type': 'boolean', "default": False}, 'bypass-rules': {'type': 'array', 'items': { 'enum': ['AzureServices', 'Logging', 'Metrics']}}, 'ip-rules': {'type': 'array', 'items': {'type': 'string'}}, 'virtual-network-rules': {'type': 'array', 'items': {'type': 'string'}} } ) def __init__(self, data, manager=None): super(StorageSetNetworkRulesAction, self).__init__(data, manager) self._log = logging.getLogger('custodian.azure.storage') self.rule_limit = 200 def _prepare_processing(self): self.client = self.manager.get_client() self.append = self.data.get('append', False) def _process_resource(self, resource): rules = self._build_ip_rules(resource, self.data.get('ip-rules', [])) # Build out the ruleset model to update the resource rule_set = NetworkRuleSet(default_action=self.data.get('default-action', 'Deny')) # If the user has too many rules log and skip if len(rules) > self.rule_limit: self._log.error("Skipped updating firewall for %s. " "%s exceeds maximum rule count of %s." % (resource['name'], len(rules), self.rule_limit)) return # Add IP rules rule_set.ip_rules = [IPRule(ip_address_or_range=r) for r in rules] # Add VNET rules vnet_rules = self._build_vnet_rules(resource, self.data.get('virtual-network-rules', [])) rule_set.virtual_network_rules = [ VirtualNetworkRule(virtual_network_resource_id=r) for r in vnet_rules] # Configure BYPASS rule_set.bypass = self._build_bypass_rules(resource, self.data.get('bypass', [])) # Update resource self.client.storage_accounts.update( resource['resourceGroup'], resource['name'], StorageAccountUpdateParameters(network_rule_set=rule_set)) def _build_bypass_rules(self, resource, new_rules): if self.append: existing_bypass = resource['properties']['networkAcls'].get('bypass', '').split(',') without_duplicates = [r for r in existing_bypass if r not in new_rules] new_rules.extend(without_duplicates) return ','.join(new_rules or ['None']) def _build_vnet_rules(self, resource, new_rules): if self.append: existing_rules = [r['id'] for r in resource['properties']['networkAcls'].get('virtualNetworkRules', [])] without_duplicates = [r for r in existing_rules if r not in new_rules] new_rules.extend(without_duplicates) return new_rules def _build_ip_rules(self, resource, new_rules): rules = [] for rule in new_rules: resolved_set = resolve_service_tag_alias(rule) if resolved_set: ranges = list(resolved_set.iter_cidrs()) for r in range(len(ranges)): if len(ranges[r]) == 1: ranges[r] = IPAddress(ranges[r].first) rules.extend(map(str, ranges)) else: rules.append(rule) if self.append: existing_rules = resource['properties']['networkAcls'].get('ipRules', []) without_duplicates = [r['value'] for r in existing_rules if r['value'] not in rules] rules.extend(without_duplicates) return rules
class StorageSetFirewallAction(SetFirewallAction): """ Set Firewall Rules Action Updates Azure Storage Firewalls and Virtual Networks settings. By default the firewall rules are appended with the new values. The ``append: False`` flag can be used to replace the old rules with the new ones on the resource. You may also reference azure public cloud Service Tags by name in place of an IP address. Use ``ServiceTags.`` followed by the ``name`` of any group from https://www.microsoft.com/en-us/download/details.aspx?id=56519. Note that there are firewall rule number limits and that you will likely need to use a regional block to fit within the limit. The limit for storage accounts is 200 rules. .. code-block:: yaml - type: set-firewall-rules bypass-rules: - Logging - Metrics ip-rules: - 11.12.13.0/16 - ServiceTags.AppService.CentralUS :example: Find storage accounts without any firewall rules. Configure default-action to ``Deny`` and then allow: - Azure Logging and Metrics services - Two specific IPs - Two subnets .. code-block:: yaml policies: - name: add-storage-firewall resource: azure.storage filters: - type: value key: properties.networkAcls.ipRules value_type: size op: eq value: 0 actions: - type: set-firewall-rules append: False bypass-rules: - Logging - Metrics ip-rules: - 11.12.13.0/16 - 21.22.23.24 virtual-network-rules: - <subnet_resource_id> - <subnet_resource_id> """ schema = type_schema('set-firewall-rules', rinherit=SetFirewallAction.schema, **{ 'default-action': { 'enum': ['Allow', 'Deny'], "default": 'Deny' }, 'bypass-rules': { 'type': 'array', 'items': { 'enum': ['AzureServices', 'Logging', 'Metrics'] } }, }) log = logging.getLogger('custodian.azure.storage.StorageSetFirewallAction') def __init__(self, data, manager=None): super(StorageSetFirewallAction, self).__init__(data, manager) self.rule_limit = 200 def _process_resource(self, resource): # Build out the ruleset model to update the resource rule_set = NetworkRuleSet( default_action=self.data.get('default-action', 'Deny')) # Add IP rules if self.data.get('ip-rules') is not None: existing_ip = [ r['value'] for r in resource['properties']['networkAcls'].get( 'ipRules', []) ] ip_rules = self._build_ip_rules(existing_ip, self.data.get('ip-rules', [])) # If the user has too many rules raise exception if len(ip_rules) > self.rule_limit: raise ValueError( "Skipped updating firewall for %s. " "%s exceeds maximum rule count of %s." % (resource['name'], len(ip_rules), self.rule_limit)) rule_set.ip_rules = [ IPRule(ip_address_or_range=r) for r in ip_rules ] # Add VNET rules if self.data.get('virtual-network-rules') is not None: existing_vnet = \ [r['id'] for r in resource['properties']['networkAcls'].get('virtualNetworkRules', [])] vnet_rules = \ self._build_vnet_rules(existing_vnet, self.data.get('virtual-network-rules', [])) rule_set.virtual_network_rules = \ [VirtualNetworkRule(virtual_network_resource_id=r) for r in vnet_rules] # Configure BYPASS if self.data.get('bypass-rules') is not None: existing_bypass = resource['properties']['networkAcls'].get( 'bypass', '').split(',') rule_set.bypass = self._build_bypass_rules( existing_bypass, self.data.get('bypass-rules', [])) # Update resource self.client.storage_accounts.update( resource['resourceGroup'], resource['name'], StorageAccountUpdateParameters(network_rule_set=rule_set))
class SetLogSettingsAction(AzureBaseAction): """Action that updates the logging settings on storage accounts. The action requires specifying an array of storage types that will be impacted by the action (blob, queue, table), retention (number in days; 0-365), and an array of log settings to enable (read, write, delete). The action will disable any settings not listed (e.g. by providing log: [write, delete], the action will disable read). :example: Enable write and delete logging and disable read logging on blob storage, and retain logs for 5 days. .. code-block:: yaml policies: - name: enable-blob-storage-logging resource: azure.storage actions: - type: set-log-settings storage-types: [blob] retention: 5 log: [write, delete] """ READ = 'read' WRITE = 'write' DELETE = 'delete' schema = type_schema('set-log-settings', required=['storage-types', 'log', 'retention'], **{ 'storage-types': { 'type': 'array', 'items': { 'type': 'string', 'enum': [BLOB_TYPE, QUEUE_TYPE, TABLE_TYPE] } }, 'log': { 'type': 'array', 'items': { 'type': 'string', 'enum': [READ, WRITE, DELETE] } }, 'retention': { 'type': 'number' } }) log = logging.getLogger('custodian.azure.storage.SetLogSettingsAction') def __init__(self, data, manager=None): super(SetLogSettingsAction, self).__init__(data, manager) self.storage_types = data['storage-types'] self.logs_to_enable = data['log'] self.retention = data['retention'] self.token = None def validate(self): if self.retention < 0 or self.retention > 365: raise PolicyValidationError( 'attribute: retention can not be less than 0 or greater than 365' ) def process_in_parallel(self, resources, event): self.token = StorageUtilities.get_storage_token(self.session) return super(SetLogSettingsAction, self).process_in_parallel(resources, event) def _process_resource(self, resource, event=None): retention = RetentionPolicy(enabled=self.retention != 0, days=self.retention) log_settings = Logging(self.DELETE in self.logs_to_enable, self.READ in self.logs_to_enable, self.WRITE in self.logs_to_enable, retention_policy=retention) for storage_type in self.storage_types: StorageSettingsUtilities.update_logging(storage_type, resource, log_settings, self.session, self.token)
class StorageDiagnosticSettingsFilter(ValueFilter): """Filters storage accounts based on its diagnostic settings. The filter requires specifying the storage type (blob, queue, table, file) and will filter based on the settings for that specific type. :example: Find all storage accounts that have a 'delete' logging setting disabled. .. code-block:: yaml policies: - name: find-accounts-with-delete-logging-disabled resource: azure.storage filters: - or: - type: storage-diagnostic-settings storage-type: blob key: logging.delete op: eq value: False - type: storage-diagnostic-settings storage-type: queue key: logging.delete op: eq value: False - type: storage-diagnostic-settings storage-type: table key: logging.delete op: eq value: False :example: Find Load Balancers that have logs for both LoadBalancerProbeHealthStatus category and LoadBalancerAlertEvent category enabled. The use of value_type: swap is important for these examples because it swaps the value and the evaluated key so that it evaluates the value provided is in the logs. .. code-block:: yaml policies: - name: find-load-balancers-with-logs-enabled resource: azure.loadbalancer filters: - type: diagnostic-settings key: logs[?category == 'LoadBalancerProbeHealthStatus'][].enabled value: True op: in value_type: swap - type: diagnostic-settings key: logs[?category == 'LoadBalancerAlertEvent'][].enabled value: True op: in value_type: swap :example: Find KeyVaults that have logs enabled for the AuditEvent category. .. code-block:: yaml policies: - name: find-keyvaults-with-logs-enabled resource: azure.keyvault filters: - type: diagnostic-settings key: logs[?category == 'AuditEvent'][].enabled value: True op: in value_type: swap """ schema = type_schema( 'storage-diagnostic-settings', rinherit=ValueFilter.schema, required=['storage-type'], **{ 'storage-type': { 'type': 'string', 'enum': [BLOB_TYPE, QUEUE_TYPE, TABLE_TYPE, FILE_TYPE] } }) log = logging.getLogger( 'custodian.azure.storage.StorageDiagnosticSettingsFilter') def __init__(self, data, manager=None): super(StorageDiagnosticSettingsFilter, self).__init__(data, manager) self.storage_type = data.get('storage-type') def process(self, resources, event=None): session = local_session(self.manager.session_factory) token = StorageUtilities.get_storage_token(session) result, errors = ThreadHelper.execute_in_parallel( resources=resources, event=event, execution_method=self.process_resource_set, executor_factory=self.executor_factory, log=self.log, session=session, token=token) return result def process_resource_set(self, resources, event=None, session=None, token=None): matched = [] for resource in resources: settings = self._get_settings(resource, session, token) filtered_settings = super(StorageDiagnosticSettingsFilter, self).process([settings], event) if filtered_settings: matched.append(resource) return matched def _get_settings(self, storage_account, session=None, token=None): storage_prefix_property = get_annotation_prefix(self.storage_type) if not (storage_prefix_property in storage_account): settings = StorageSettingsUtilities.get_settings( self.storage_type, storage_account, session, token) storage_account[storage_prefix_property] = json.loads( jsonpickle.encode(settings)) return storage_account[storage_prefix_property]
class SetFirewallAction(AzureBaseAction): schema = type_schema('set-firewall-rules', required=[], **{ 'append': { 'type': 'boolean', 'default': True }, 'bypass-rules': { 'type': 'array' }, 'ip-rules': { 'type': 'array', 'items': { 'type': 'string' } }, 'virtual-network-rules': { 'type': 'array', 'items': { 'type': 'string' } } }) @abstractmethod def __init__(self, data, manager=None): super(SetFirewallAction, self).__init__(data, manager) def _prepare_processing(self): self.client = self.manager.get_client() self.append = self.data.get('append', True) @abstractmethod def _process_resource(self, resource): pass def _build_bypass_rules(self, existing_bypass, new_rules): if self.append: without_duplicates = [ r for r in existing_bypass if r not in new_rules ] new_rules.extend(without_duplicates) return ','.join(new_rules or ['None']) def _build_vnet_rules(self, existing_vnet, new_rules): if self.append: without_duplicates = [ r for r in existing_vnet if r not in new_rules ] new_rules.extend(without_duplicates) return new_rules def _build_ip_rules(self, existing_ip, new_rules): rules = [] for rule in new_rules: # attempt to resolve this rule as a service tag alias # if it isn't a valid alias then we'll get `None` back. resolved_set = resolve_service_tag_alias(rule) if resolved_set: # this is a service tag alias, so we need to insert the whole # aliased array into the ruleset ranges = list(resolved_set.iter_cidrs()) for r in range(len(ranges)): if len(ranges[r]) == 1: ranges[r] = IPAddress(ranges[r].first) rules.extend(map(str, ranges)) else: # just a normal rule, append rules.append(rule) if self.append: for ip in existing_ip: if str(ip) not in rules: rules.append(str(ip)) return rules
class VMExtensionsFilter(ValueFilter): """ Provides a value filter targetting the virtual machine extensions array. Requires an additional API call per virtual machine to retrieve the extensions. Here is an example of the data returned: .. code-block:: json [{ "id": "/subscriptions/...", "name": "CustomScript", "type": "Microsoft.Compute/virtualMachines/extensions", "location": "centralus", "properties": { "publisher": "Microsoft.Azure.Extensions", "type": "CustomScript", "typeHandlerVersion": "2.0", "autoUpgradeMinorVersion": true, "settings": { "fileUris": [] }, "provisioningState": "Succeeded" } }] :examples: Find VM's with Custom Script extensions .. code-block:: yaml policies: - name: vm-with-customscript description: | Find all virtual machines with a custom script extension installed. resource: azure.vm filters: - type: vm-extensions op: in key: "[].properties.type" value: CustomScript value_type: swap Find VM's without the OMS agent installed .. code-block:: yaml policies: - name: vm-without-oms description: | Find all virtual machines without the OMS agent installed. resource: azure.vm filters: - type: vm-extensions op: not-in key: "[].properties.type" value: OmsAgentForLinux value_type: swap """ schema = type_schema('vm-extensions', rinherit=ValueFilter.schema) annotate = False # cannot annotate arrays def __call__(self, i): if 'c7n:vm-extensions' not in i: client = self.manager.get_client() extensions = (client.virtual_machine_extensions.list( i['resourceGroup'], i['name'])) i['c7n:vm-extensions'] = [ e.serialize(True) for e in extensions.value ] return super(VMExtensionsFilter, self).__call__(i['c7n:vm-extensions'])
class StorageDiagnosticSettingsFilter(ValueFilter): """Filters storage accounts based on its diagnostic settings. The filter requires specifying the storage type (blob, queue, table, file) and will filter based on the settings for that specific type. :example: Find all storage accounts that have a 'delete' logging setting disabled. .. code-block:: yaml policies: - name: find-accounts-with-delete-logging-disabled resource: azure.storage filters: - or: - type: storage-diagnostic-settings storage-type: blob key: logging.delete op: eq value: False - type: storage-diagnostic-settings storage-type: queue key: logging.delete op: eq value: False - type: storage-diagnostic-settings storage-type: table key: logging.delete op: eq value: False """ schema = type_schema( 'storage-diagnostic-settings', rinherit=ValueFilter.schema, required=['storage-type'], **{ 'storage-type': { 'type': 'string', 'enum': [BLOB_TYPE, QUEUE_TYPE, TABLE_TYPE, FILE_TYPE] } }) def __init__(self, data, manager=None): super(StorageDiagnosticSettingsFilter, self).__init__(data, manager) self.storage_type = data.get('storage-type') self.log = logging.getLogger('custodian.azure.storage') def process(self, resources, event=None): session = local_session(self.manager.session_factory) token = StorageUtilities.get_storage_token(session) result, errors = ThreadHelper.execute_in_parallel( resources=resources, event=event, execution_method=self.process_resource_set, executor_factory=self.executor_factory, log=self.log, session=session, token=token) return result def process_resource_set(self, resources, event=None, session=None, token=None): matched = [] for resource in resources: settings = self._get_settings(resource, session, token) filtered_settings = super(StorageDiagnosticSettingsFilter, self).process([settings], event) if filtered_settings: matched.append(resource) return matched def _get_settings(self, storage_account, session=None, token=None): storage_prefix_property = get_annotation_prefix(self.storage_type) if not (storage_prefix_property in storage_account): settings = StorageSettingsUtilities.get_settings( self.storage_type, storage_account, session, token) storage_account[storage_prefix_property] = json.loads( jsonpickle.encode(settings)) return storage_account[storage_prefix_property]
class StorageSetNetworkRulesAction(AzureBaseAction): """ Set Network Rules Action Updates Azure Storage Firewalls and Virtual Networks settings. :example: Find storage accounts without any firewall rules. Configure default-action to ``Deny`` and then allow: - Azure Logging and Metrics services - Two specific IPs - Two subnets .. code-block:: yaml policies: - name: add-storage-firewall resource: azure.storage filters: - type: value key: properties.networkAcls.ipRules value_type: size op: eq value: 0 actions: - type: set-network-rules default-action: Deny bypass: [Logging, Metrics] ip-rules: - ip-address-or-range: 11.12.13.14 - ip-address-or-range: 21.22.23.24 virtual-network-rules: - virtual-network-resource-id: <subnet_resource_id> - virtual-network-resource-id: <subnet_resource_id> """ schema = type_schema('set-network-rules', required=['default-action'], **{ 'default-action': { 'enum': ['Allow', 'Deny'] }, 'bypass': { 'type': 'array', 'items': { 'enum': ['AzureServices', 'Logging', 'Metrics'] } }, 'ip-rules': { 'type': 'array', 'items': { 'ip-address-or-range': { 'type': 'string' } } }, 'virtual-network-rules': { 'type': 'array', 'items': { 'virtual-network-resource-id': { 'type': 'string' } } } }) def _prepare_processing(self, ): self.client = self.manager.get_client() def _process_resource(self, resource): rule_set = NetworkRuleSet(default_action=self.data['default-action']) if 'ip-rules' in self.data: rule_set.ip_rules = [ IPRule(ip_address_or_range=r['ip-address-or-range'], action='Allow') # 'Allow' is the only allowed action for r in self.data['ip-rules'] ] if 'virtual-network-rules' in self.data: rule_set.virtual_network_rules = [ VirtualNetworkRule( virtual_network_resource_id=r[ 'virtual-network-resource-id'], action='Allow') # 'Allow' is the only allowed action for r in self.data['virtual-network-rules'] ] if len(self.data.get('bypass', [])) > 0: rule_set.bypass = '******'.join(self.data['bypass']) else: rule_set.bypass = '******' self.client.storage_accounts.update( resource['resourceGroup'], resource['name'], StorageAccountUpdateParameters(network_rule_set=rule_set))
class NetworkInterfaceFilter(RelatedResourceFilter): schema = type_schema('network-interface', rinherit=ValueFilter.schema) RelatedResource = "c7n_azure.resources.network_interface.NetworkInterface" RelatedIdsExpression = "properties.networkProfile.networkInterfaces[0].id"
class SqlServerFirewallRulesFilter(Filter): """Filters SQL servers by the firewall rules :example: .. code-block:: yaml policies: - name: servers-with-firewall resource: azure.sqlserver filters: - type: firewall-rules include: - '131.107.160.2-131.107.160.3' - 10.20.20.0/24 """ schema = type_schema( 'firewall-rules', **{ 'include': {'type': 'array', 'items': {'type': 'string'}}, 'equal': {'type': 'array', 'items': {'type': 'string'}} }) def __init__(self, data, manager=None): super(SqlServerFirewallRulesFilter, self).__init__(data, manager) self.policy_include = None self.policy_equal = None self.client = None def validate(self): self.policy_include = IpRangeHelper.parse_ip_ranges(self.data, 'include') self.policy_equal = IpRangeHelper.parse_ip_ranges(self.data, 'equal') has_include = self.policy_include is not None has_equal = self.policy_equal is not None if has_include and has_equal: raise FilterValidationError('Cannot have both include and equal.') if not has_include and not has_equal: raise FilterValidationError('Must have either include or equal.') return True def process(self, resources, event=None): self.client = self.manager.get_client() result, _ = ThreadHelper.execute_in_parallel( resources=resources, event=event, execution_method=self._check_resources, executor_factory=self.executor_factory, log=log ) return result def _check_resources(self, resources, event): return [r for r in resources if self._check_resource(r)] def _check_resource(self, resource): try: query = self.client.firewall_rules.list_by_server( resource['resourceGroup'], resource['name']) resource_rules = set([IPRange(r.start_ip_address, r.end_ip_address) for r in query]) except Exception as error: log.warning(error) return False ok = self._check_rules(resource_rules) return ok def _check_rules(self, resource_rules): if self.policy_equal is not None: return self.policy_equal == resource_rules elif self.policy_include is not None: return self.policy_include.issubset(resource_rules) else: # validated earlier, can never happen raise FilterValidationError("Internal error.")