yunion.io/x/cloudmux@v0.3.10-0-alpha.1/pkg/multicloud/azure/waf.go (about)

     1  // Copyright 2019 Yunion
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package azure
    16  
    17  import (
    18  	"fmt"
    19  	"net/url"
    20  	"strings"
    21  
    22  	"yunion.io/x/jsonutils"
    23  	"yunion.io/x/log"
    24  	"yunion.io/x/pkg/errors"
    25  
    26  	api "yunion.io/x/cloudmux/pkg/apis/compute"
    27  	"yunion.io/x/cloudmux/pkg/cloudprovider"
    28  	"yunion.io/x/cloudmux/pkg/multicloud"
    29  )
    30  
    31  type SMatchvariable struct {
    32  	Variablename string `json:"variableName"`
    33  	Selector     string `json:"selector"`
    34  }
    35  
    36  type SMatchcondition struct {
    37  	Matchvariables   []SMatchvariable `json:"matchVariables"`
    38  	Operator         string           `json:"operator"`
    39  	Negationconditon bool             `json:"negationConditon"`
    40  	Matchvalues      []string         `json:"matchValues"`
    41  	Transforms       []string         `json:"transforms"`
    42  }
    43  
    44  type CustomRule struct {
    45  	waf *SAppGatewayWaf
    46  
    47  	Name     string `json:"name"`
    48  	Priority int    `json:"priority"`
    49  	Ruletype string `json:"ruleType"`
    50  	//RateLimitThreshold *int              `json:"rateLimitThreshold"`
    51  	Matchconditions []SMatchcondition `json:"matchConditions"`
    52  	Action          string            `json:"action"`
    53  }
    54  
    55  func (self *CustomRule) GetName() string {
    56  	return self.Name
    57  }
    58  
    59  func (self *CustomRule) GetGlobalId() string {
    60  	return fmt.Sprintf("%s-%s", self.waf.GetGlobalId(), self.GetName())
    61  }
    62  
    63  func (self *CustomRule) GetDesc() string {
    64  	return ""
    65  }
    66  
    67  func (self *CustomRule) GetPriority() int {
    68  	return self.Priority
    69  }
    70  
    71  func (self *CustomRule) Delete() error {
    72  	rules := []CustomRule{}
    73  	for _, rule := range self.waf.Properties.Customrules {
    74  		if rule.Name != self.Name {
    75  			rules = append(rules, rule)
    76  		}
    77  	}
    78  	self.waf.Properties.Customrules = rules
    79  	return self.waf.region.update(jsonutils.Marshal(self.waf), nil)
    80  }
    81  
    82  func wafMatchFieldAndKeyLocal2Cloud(opts cloudprovider.SWafStatement) ([]SMatchvariable, error) {
    83  	ret := []SMatchvariable{}
    84  	switch opts.MatchField {
    85  	case cloudprovider.WafMatchFieldQuery:
    86  		ret = append(ret, SMatchvariable{
    87  			Variablename: "QueryString",
    88  		})
    89  	case cloudprovider.WafMatchFieldMethod:
    90  		ret = append(ret, SMatchvariable{
    91  			Variablename: "RequestMethod",
    92  		})
    93  	case cloudprovider.WafMatchFiledUriPath:
    94  		ret = append(ret, SMatchvariable{
    95  			Variablename: "RequestUri",
    96  		})
    97  	case cloudprovider.WafMatchFiledHeader:
    98  		ret = append(ret, SMatchvariable{
    99  			Variablename: "RequestHeaders",
   100  			Selector:     opts.MatchFieldKey,
   101  		})
   102  	case cloudprovider.WafMatchFiledPostArgs:
   103  		ret = append(ret, SMatchvariable{
   104  			Variablename: "PostArgs",
   105  			Selector:     opts.MatchFieldKey,
   106  		})
   107  	case cloudprovider.WafMatchFieldBody:
   108  		ret = append(ret, SMatchvariable{
   109  			Variablename: "RequestBody",
   110  		})
   111  	case cloudprovider.WafMatchFiledCookie:
   112  		ret = append(ret, SMatchvariable{
   113  			Variablename: "RequestCookies",
   114  			Selector:     opts.MatchFieldKey,
   115  		})
   116  	default:
   117  		return ret, fmt.Errorf("unsupported match filed %s", opts.MatchField)
   118  	}
   119  	return ret, nil
   120  }
   121  
   122  func wafMatchFieldAndKeyCloud2Local(v SMatchvariable) (cloudprovider.TWafMatchField, string, error) {
   123  	switch v.Variablename {
   124  	case "QueryString":
   125  		return cloudprovider.WafMatchFieldQuery, v.Selector, nil
   126  	case "RequestMethod":
   127  		return cloudprovider.WafMatchFieldMethod, "", nil
   128  	case "RequestUri":
   129  		return cloudprovider.WafMatchFiledUriPath, "", nil
   130  	case "RequestHeaders":
   131  		return cloudprovider.WafMatchFiledHeader, v.Selector, nil
   132  	case "PostArgs":
   133  		return cloudprovider.WafMatchFiledPostArgs, v.Selector, nil
   134  	case "RequestBody":
   135  		return cloudprovider.WafMatchFieldBody, "", nil
   136  	case "RequestCookies":
   137  		return cloudprovider.WafMatchFiledCookie, v.Selector, nil
   138  	case "RemoteAddr":
   139  		return cloudprovider.WafMatchFiledHeader, v.Selector, nil
   140  	default:
   141  		return "", "", fmt.Errorf("invalid variablename %s", v.Variablename)
   142  	}
   143  }
   144  
   145  func wafStatementLocal2Cloud(opts cloudprovider.SWafStatement) (SMatchcondition, error) {
   146  	ret := SMatchcondition{}
   147  	if opts.Transformations != nil {
   148  		for _, tran := range *opts.Transformations {
   149  			ret.Transforms = append(ret.Transforms, string(tran))
   150  		}
   151  	}
   152  	if opts.MatchFieldValues != nil {
   153  		ret.Matchvalues = *opts.MatchFieldValues
   154  	}
   155  	ret.Negationconditon = opts.Negation
   156  	ret.Operator = string(opts.Operator)
   157  	var err error
   158  	switch opts.Type {
   159  	case cloudprovider.WafStatementTypeIPSet:
   160  		ret.Operator = "IPMatch"
   161  		ret.Matchvariables = []SMatchvariable{
   162  			SMatchvariable{
   163  				Variablename: "RemoteAddr",
   164  			},
   165  		}
   166  	case cloudprovider.WafStatementTypeGeoMatch:
   167  		ret.Operator = "GeoMatch"
   168  		if len(opts.ForwardedIPHeader) == 0 {
   169  			ret.Matchvariables = []SMatchvariable{
   170  				SMatchvariable{
   171  					Variablename: "RemoteAddr",
   172  				},
   173  			}
   174  		} else {
   175  			ret.Matchvariables = []SMatchvariable{
   176  				SMatchvariable{
   177  					Variablename: "RequestHeaders",
   178  					Selector:     opts.ForwardedIPHeader,
   179  				},
   180  			}
   181  		}
   182  	case cloudprovider.WafStatementTypeSize:
   183  		switch opts.Operator {
   184  		case "LT":
   185  			ret.Operator = "LessThan"
   186  		case "LE":
   187  			ret.Operator = "LessThanOrEqual"
   188  		case "GT":
   189  			ret.Operator = "GreaterThan"
   190  		default:
   191  			return ret, fmt.Errorf("invalid operator %s for %s", opts.Operator, opts.Type)
   192  		}
   193  		ret.Matchvariables, err = wafMatchFieldAndKeyLocal2Cloud(opts)
   194  		if err != nil {
   195  			return ret, errors.Wrapf(err, "wafMatchFieldAndKeyLocal2Cloud")
   196  		}
   197  	case cloudprovider.WafStatementTypeByteMatch:
   198  		switch opts.Operator {
   199  		case "Contains", "EndsWith", "Regex":
   200  		case "StartsWith":
   201  			ret.Operator = "BeginsWith"
   202  		case "Exactly":
   203  			ret.Operator = "Equal"
   204  		default:
   205  			return ret, fmt.Errorf("invalid operator %s for %s", opts.Operator, opts.Type)
   206  		}
   207  		ret.Matchvariables, err = wafMatchFieldAndKeyLocal2Cloud(opts)
   208  		if err != nil {
   209  			return ret, errors.Wrapf(err, "wafMatchFieldAndKeyLocal2Cloud")
   210  		}
   211  	}
   212  	return ret, nil
   213  }
   214  
   215  func wafRuleLocal2Cloud(opts *cloudprovider.SWafRule) (*CustomRule, error) {
   216  	ret := &CustomRule{}
   217  	ret.Name = opts.Name
   218  	ret.Priority = opts.Priority
   219  	ret.Ruletype = "MatchRule"
   220  	ret.Matchconditions = []SMatchcondition{}
   221  	for _, s := range opts.Statements {
   222  		cds, err := wafStatementLocal2Cloud(s)
   223  		if err != nil {
   224  			return nil, errors.Wrapf(err, "wafStatementLocal2Cloud")
   225  		}
   226  		ret.Matchconditions = append(ret.Matchconditions, cds)
   227  	}
   228  	ret.Action = "Block"
   229  	if opts.Action != nil {
   230  		ret.Action = string(opts.Action.Action)
   231  	}
   232  	return ret, nil
   233  }
   234  
   235  func (self *CustomRule) Update(opts *cloudprovider.SWafRule) error {
   236  	rules := []CustomRule{}
   237  	for _, rule := range self.waf.Properties.Customrules {
   238  		if rule.Name != self.Name {
   239  			rules = append(rules, rule)
   240  		} else {
   241  			rule, err := wafRuleLocal2Cloud(opts)
   242  			if err != nil {
   243  				return errors.Wrapf(err, "wafRuleLocal2Cloud")
   244  			}
   245  			rules = append(rules, *rule)
   246  		}
   247  	}
   248  	self.waf.Properties.Customrules = rules
   249  	return self.waf.region.update(jsonutils.Marshal(self.waf), nil)
   250  }
   251  
   252  func (self *CustomRule) GetAction() *cloudprovider.DefaultAction {
   253  	return &cloudprovider.DefaultAction{
   254  		Action: cloudprovider.TWafAction(self.Action),
   255  	}
   256  }
   257  
   258  func (self *CustomRule) GetStatementCondition() cloudprovider.TWafStatementCondition {
   259  	return cloudprovider.WafStatementConditionAnd
   260  }
   261  
   262  func (self *CustomRule) GetStatements() ([]cloudprovider.SWafStatement, error) {
   263  	ret := []cloudprovider.SWafStatement{}
   264  	for _, condition := range self.Matchconditions {
   265  		trans := cloudprovider.TextTransformations{}
   266  		for _, tran := range condition.Transforms {
   267  			trans = append(trans, cloudprovider.TWafTextTransformation(tran))
   268  		}
   269  		values := cloudprovider.TWafMatchFieldValues(condition.Matchvalues)
   270  		statement := cloudprovider.SWafStatement{
   271  			Negation:         condition.Negationconditon,
   272  			Transformations:  &trans,
   273  			MatchFieldValues: &values,
   274  		}
   275  		switch condition.Operator {
   276  		case "IPMatch":
   277  			statement.Type = cloudprovider.WafStatementTypeIPSet
   278  		case "GeoMatch":
   279  			statement.Type = cloudprovider.WafStatementTypeGeoMatch
   280  		case "LessThan":
   281  			statement.Type = cloudprovider.WafStatementTypeSize
   282  			statement.Operator = cloudprovider.WafOperatorLT
   283  		case "LessThanOrEqual":
   284  			statement.Type = cloudprovider.WafStatementTypeSize
   285  			statement.Operator = cloudprovider.WafOperatorLE
   286  		case "GreaterThan":
   287  			statement.Type = cloudprovider.WafStatementTypeSize
   288  			statement.Operator = cloudprovider.WafOperatorGT
   289  		case "BeginsWith":
   290  			statement.Type = cloudprovider.WafStatementTypeByteMatch
   291  			statement.Operator = cloudprovider.WafOperatorStartsWith
   292  		case "Contains", "EndsWith", "Regex":
   293  			statement.Type = cloudprovider.WafStatementTypeByteMatch
   294  			statement.Operator = cloudprovider.TWafOperator(condition.Operator)
   295  		case "Equal":
   296  			statement.Type = cloudprovider.WafStatementTypeByteMatch
   297  			statement.Operator = cloudprovider.WafOperatorExactly
   298  		default:
   299  			statement.Type = cloudprovider.WafStatementTypeByteMatch
   300  		}
   301  
   302  		var err error
   303  		for _, v := range condition.Matchvariables {
   304  			statement.MatchField, statement.MatchFieldKey, err = wafMatchFieldAndKeyCloud2Local(v)
   305  			if err != nil {
   306  				log.Errorf("wafMatchFieldAndKeyCloud2Local %s error: %v", v, err)
   307  				continue
   308  			}
   309  			ret = append(ret, statement)
   310  		}
   311  	}
   312  	return ret, nil
   313  }
   314  
   315  type ManagedRule struct {
   316  	Rulesettype    string `json:"ruleSetType"`
   317  	Rulesetversion string `json:"ruleSetVersion"`
   318  }
   319  
   320  type ManagedRules struct {
   321  	waf             *SAppGatewayWaf
   322  	Managedrulesets []ManagedRule `json:"managedRuleSets"`
   323  }
   324  
   325  func (self *ManagedRules) GetName() string {
   326  	return fmt.Sprintf("%s Managed rules", self.waf.GetName())
   327  }
   328  
   329  func (self *ManagedRules) GetGlobalId() string {
   330  	return self.waf.GetGlobalId()
   331  }
   332  
   333  func (self *ManagedRules) GetDesc() string {
   334  	return ""
   335  }
   336  
   337  func (self *ManagedRules) GetPriority() int {
   338  	return 0
   339  }
   340  
   341  func (self *ManagedRules) GetAction() *cloudprovider.DefaultAction {
   342  	return nil
   343  }
   344  
   345  func (self *ManagedRules) Delete() error {
   346  	return cloudprovider.ErrNotSupported
   347  }
   348  
   349  func (self *ManagedRules) Update(opts *cloudprovider.SWafRule) error {
   350  	rules := []ManagedRule{}
   351  	for _, s := range opts.Statements {
   352  		if len(s.ManagedRuleGroupName) == 0 {
   353  			return fmt.Errorf("missing managed rule group name")
   354  		}
   355  		names := strings.Split(s.ManagedRuleGroupName, "_")
   356  		if len(names) != 2 {
   357  			return fmt.Errorf("invalid managed rule group name %s", s.ManagedRuleGroupName)
   358  		}
   359  		rules = append(rules, ManagedRule{
   360  			Rulesettype:    names[0],
   361  			Rulesetversion: names[1],
   362  		})
   363  	}
   364  	if len(rules) == 0 {
   365  		return fmt.Errorf("missing statements")
   366  	}
   367  	self.waf.Properties.Managedrules = ManagedRules{
   368  		Managedrulesets: rules,
   369  	}
   370  	return self.waf.region.update(jsonutils.Marshal(self.waf), nil)
   371  }
   372  
   373  func (self *ManagedRules) GetStatementCondition() cloudprovider.TWafStatementCondition {
   374  	return cloudprovider.WafStatementConditionAnd
   375  }
   376  
   377  func (self *ManagedRules) GetStatements() ([]cloudprovider.SWafStatement, error) {
   378  	ret := []cloudprovider.SWafStatement{}
   379  	for i := range self.Managedrulesets {
   380  		ruleGroupName := fmt.Sprintf("%s_%s", self.Managedrulesets[i].Rulesettype, self.Managedrulesets[i].Rulesetversion)
   381  		ret = append(ret, cloudprovider.SWafStatement{
   382  			ManagedRuleGroupName: ruleGroupName,
   383  			Type:                 cloudprovider.WafStatementTypeManagedRuleGroup,
   384  			RuleGroupId:          ruleGroupName,
   385  		})
   386  	}
   387  	return ret, nil
   388  }
   389  
   390  type SAppGatewayWaf struct {
   391  	multicloud.SResourceBase
   392  	AzureTags
   393  	region *SRegion
   394  
   395  	Name       string `json:"name"`
   396  	ID         string `json:"id"`
   397  	Type       string `json:"type"`
   398  	Location   string `json:"location"`
   399  	Properties struct {
   400  		ApplicationGateways []SApplicationGateway
   401  		HttpListeners       []struct {
   402  			Id string
   403  		}
   404  		PathBasedRules []struct {
   405  			Id string
   406  		}
   407  		Resourcestate     string `json:"resourceState"`
   408  		Provisioningstate string `json:"provisioningState"`
   409  		Policysettings    struct {
   410  			State                  string `json:"state"`
   411  			Mode                   string `json:"mode"`
   412  			Maxrequestbodysizeinkb int    `json:"maxRequestBodySizeInKb"`
   413  			Fileuploadlimitinmb    int    `json:"fileUploadLimitInMb"`
   414  			Requestbodycheck       bool   `json:"requestBodyCheck"`
   415  		} `json:"policySettings"`
   416  		Customrules  []CustomRule `json:"customRules"`
   417  		Managedrules ManagedRules `json:"managedRules"`
   418  	} `json:"properties"`
   419  }
   420  
   421  func (self *SAppGatewayWaf) GetEnabled() bool {
   422  	return self.Properties.Policysettings.State == "Enabled"
   423  }
   424  
   425  func (self *SAppGatewayWaf) GetName() string {
   426  	return self.Name
   427  }
   428  
   429  func (self *SAppGatewayWaf) GetId() string {
   430  	return self.ID
   431  }
   432  
   433  func (self *SAppGatewayWaf) GetGlobalId() string {
   434  	return strings.ToLower(self.ID)
   435  }
   436  
   437  func (self *SAppGatewayWaf) Delete() error {
   438  	return self.region.del(self.ID)
   439  }
   440  
   441  func (self *SAppGatewayWaf) GetWafType() cloudprovider.TWafType {
   442  	return cloudprovider.WafTypeAppGateway
   443  }
   444  
   445  func (self *SAppGatewayWaf) AddRule(opts *cloudprovider.SWafRule) (cloudprovider.ICloudWafRule, error) {
   446  	rule, err := wafRuleLocal2Cloud(opts)
   447  	if err != nil {
   448  		return nil, errors.Wrapf(err, "wafRuleLocal2Cloud")
   449  	}
   450  	rule.waf = self
   451  	self.Properties.Customrules = append(self.Properties.Customrules, *rule)
   452  	err = self.region.update(jsonutils.Marshal(self), nil)
   453  	if err != nil {
   454  		return nil, errors.Wrapf(err, "update")
   455  	}
   456  	return rule, nil
   457  }
   458  
   459  func (self *SAppGatewayWaf) GetStatus() string {
   460  	switch self.Properties.Provisioningstate {
   461  	case "Deleting":
   462  		return api.WAF_STATUS_DELETING
   463  	case "Failed":
   464  		return api.WAF_STATUS_CREATE_FAILED
   465  	case "Succeeded":
   466  		return api.WAF_STATUS_AVAILABLE
   467  	case "Updating":
   468  		return api.WAF_STATUS_UPDATING
   469  	default:
   470  		return self.Properties.Provisioningstate
   471  	}
   472  }
   473  
   474  func (self *SAppGatewayWaf) GetRules() ([]cloudprovider.ICloudWafRule, error) {
   475  	ret := []cloudprovider.ICloudWafRule{}
   476  	for i := range self.Properties.Customrules {
   477  		self.Properties.Customrules[i].waf = self
   478  		ret = append(ret, &self.Properties.Customrules[i])
   479  	}
   480  	self.Properties.Managedrules.waf = self
   481  	ret = append(ret, &self.Properties.Managedrules)
   482  	return ret, nil
   483  }
   484  
   485  func (self *SAppGatewayWaf) Refresh() error {
   486  	waf, err := self.region.GetAppGatewayWaf(self.ID)
   487  	if err != nil {
   488  		return errors.Wrapf(err, "GetAppGatewayWa")
   489  	}
   490  	return jsonutils.Update(self, waf)
   491  }
   492  
   493  func (self *SAppGatewayWaf) GetDefaultAction() *cloudprovider.DefaultAction {
   494  	return &cloudprovider.DefaultAction{
   495  		Action: cloudprovider.TWafAction(self.Properties.Policysettings.Mode),
   496  	}
   497  }
   498  
   499  func (self *SRegion) ListAppWafs() ([]SAppGatewayWaf, error) {
   500  	ret := []SAppGatewayWaf{}
   501  	err := self.list("Microsoft.Network/ApplicationGatewayWebApplicationFirewallPolicies", url.Values{}, &ret)
   502  	if err != nil {
   503  		return nil, errors.Wrapf(err, "list")
   504  	}
   505  	return ret, nil
   506  }
   507  
   508  type SAppWafRuleGroup struct {
   509  	Name       string `json:"name"`
   510  	ID         string `json:"id"`
   511  	Type       string `json:"type"`
   512  	Properties struct {
   513  		Provisioningstate string `json:"provisioningState"`
   514  		Rulesettype       string `json:"ruleSetType"`
   515  		Rulesetversion    string `json:"ruleSetVersion"`
   516  		Rulegroups        []struct {
   517  			Rulegroupname string `json:"ruleGroupName"`
   518  			Description   string `json:"description"`
   519  			Rules         []struct {
   520  				Ruleid      int    `json:"ruleId"`
   521  				Description string `json:"description"`
   522  			} `json:"rules"`
   523  		} `json:"ruleGroups"`
   524  	} `json:"properties"`
   525  }
   526  
   527  func (self *SRegion) CreateICloudWafInstance(opts *cloudprovider.WafCreateOptions) (cloudprovider.ICloudWafInstance, error) {
   528  	switch opts.Type {
   529  	case cloudprovider.WafTypeAppGateway:
   530  		return self.CreateAppWafInstance(opts.Name, opts.DefaultAction)
   531  	default:
   532  		return nil, errors.Wrapf(cloudprovider.ErrNoSuchProvder, "invalid waf type %s", opts.Type)
   533  	}
   534  }
   535  
   536  func (self *SRegion) GetICloudWafInstanceById(id string) (cloudprovider.ICloudWafInstance, error) {
   537  	if strings.Contains(id, "microsoft.network/applicationgatewaywebapplicationfirewallpolicies") {
   538  		return self.GetAppGatewayWaf(id)
   539  	}
   540  	return nil, errors.Wrapf(cloudprovider.ErrNotSupported, id)
   541  }
   542  
   543  func (self *SRegion) CreateAppWafInstance(name string, action *cloudprovider.DefaultAction) (*SAppGatewayWaf, error) {
   544  	mode := cloudprovider.WafActionDetection
   545  	if action != nil {
   546  		switch action.Action {
   547  		case cloudprovider.WafActionDetection, cloudprovider.WafActionPrevention:
   548  			mode = action.Action
   549  		default:
   550  			return nil, errors.Wrapf(cloudprovider.ErrNotSupported, "invalid action %s", action.Action)
   551  		}
   552  	}
   553  	params := map[string]interface{}{
   554  		"Type":     "Microsoft.Network/applicationGatewayWebApplicationFirewallPolicies",
   555  		"Name":     name,
   556  		"Location": self.Name,
   557  		"properties": map[string]interface{}{
   558  			"customRules": []string{},
   559  			"policySettings": map[string]interface{}{
   560  				"fileUploadLimitInMb":    100,
   561  				"maxRequestBodySizeInKb": 128,
   562  				"mode":                   mode,
   563  				"requestBodyCheck":       true,
   564  				"state":                  "Enabled",
   565  			},
   566  			"managedRules": map[string]interface{}{
   567  				"exclusions": []string{},
   568  				"managedRuleSets": []map[string]interface{}{
   569  					map[string]interface{}{
   570  						"ruleSetType":        "OWASP",
   571  						"ruleSetVersion":     "3.1",
   572  						"ruleGroupOverrides": []string{},
   573  					},
   574  				},
   575  			},
   576  		},
   577  	}
   578  	ret := &SAppGatewayWaf{region: self}
   579  	err := self.create("", jsonutils.Marshal(params), ret)
   580  	if err != nil {
   581  		return nil, err
   582  	}
   583  	return ret, nil
   584  }
   585  
   586  func (self *SRegion) GetAppGatewayWaf(id string) (*SAppGatewayWaf, error) {
   587  	res := &SAppGatewayWaf{region: self}
   588  	return res, self.get(id, nil, &res)
   589  }
   590  
   591  func (self *SRegion) ListAppWafManagedRuleGroup() ([]SAppWafRuleGroup, error) {
   592  	ret := []SAppWafRuleGroup{}
   593  	err := self.list("Microsoft.Network/applicationGatewayAvailableWafRuleSets", url.Values{}, &ret)
   594  	if err != nil {
   595  		return nil, errors.Wrapf(err, "list")
   596  	}
   597  	return ret, nil
   598  }
   599  
   600  func (self *SRegion) GetICloudWafInstances() ([]cloudprovider.ICloudWafInstance, error) {
   601  	wafs, err := self.ListAppWafs()
   602  	if err != nil {
   603  		return nil, errors.Wrapf(err, "ListAppWafs")
   604  	}
   605  	ret := []cloudprovider.ICloudWafInstance{}
   606  	for i := range wafs {
   607  		wafs[i].region = self
   608  		ret = append(ret, &wafs[i])
   609  	}
   610  	return ret, nil
   611  }
   612  
   613  func (self *SAppGatewayWaf) GetCloudResources() ([]cloudprovider.SCloudResource, error) {
   614  	ret := []cloudprovider.SCloudResource{}
   615  	for _, ag := range self.Properties.ApplicationGateways {
   616  		ret = append(ret, cloudprovider.SCloudResource{
   617  			Id:            ag.Id,
   618  			Name:          ag.Id[strings.LastIndex(ag.Id, "/")+1:],
   619  			Type:          "Application Gateway",
   620  			CanDissociate: true,
   621  		})
   622  	}
   623  	for _, lis := range self.Properties.HttpListeners {
   624  		ret = append(ret, cloudprovider.SCloudResource{
   625  			Id:            lis.Id,
   626  			Name:          lis.Id[strings.LastIndex(lis.Id, "/")+1:],
   627  			Type:          "HTTP Listener",
   628  			CanDissociate: true,
   629  		})
   630  	}
   631  	for _, route := range self.Properties.PathBasedRules {
   632  		ret = append(ret, cloudprovider.SCloudResource{
   633  			Id:            route.Id,
   634  			Name:          route.Id[strings.LastIndex(route.Id, "/")+1:],
   635  			Type:          "Route Path",
   636  			CanDissociate: true,
   637  		})
   638  
   639  	}
   640  	return ret, nil
   641  }
   642  
   643  func (self *SAppGatewayWaf) SetTags(tags map[string]string, replace bool) error {
   644  	if !replace {
   645  		for k, v := range self.Tags {
   646  			if _, ok := tags[k]; !ok {
   647  				tags[k] = v
   648  			}
   649  		}
   650  	}
   651  	_, err := self.region.client.SetTags(self.ID, tags)
   652  	if err != nil {
   653  		return errors.Wrapf(err, "SetTags")
   654  	}
   655  	return nil
   656  }