Repository: kubernetes-sigs/cloud-provider-azure
Pull request #3019 (2023-01-04 02:42:25)

[Doc] Add Service Azure LB IPv4 & IPv6 to LB doc
Signed-off-by: Zhecheng Li <zhechengli@microsoft.com>

<!--  Thanks for sending a pull request!  Here are some tips for you:

1. If this is your first time, please read our contributor guidelines: https://git.k8s.io/community/contributors/guide/first-contribution.md#your-first-contribution and developer guide https://git.k8s.io/community/contributors/devel/development.md#development-guide
2. Please label this pull request according to what type of issue you are addressing, especially if this is a release targeted pull request. For reference on required PR/issue labels, read here:
https://git.k8s.io/community/contributors/devel/sig-release/release.md#issuepr-kind-label
3. Ensure you have added or ran the appropriate tests for your PR: https://git.k8s.io/community/contributors/devel/sig-testing/testing.md
4. If you want *faster* PR reviews, read how: https://git.k8s.io/community/contributors/guide/pull-requests.md#best-practices-for-faster-reviews
5. If the PR is unfinished, see how to mark it: https://git.k8s.io/community/contributors/guide/pull-requests.md#marking-unfinished-pull-requests
-->

#### What type of PR is this?
/kind documentation
<!--
Add one of the following kinds:
/kind bug
/kind cleanup
/kind documentation
/kind feature
/kind design

Optionally add one or more of the following kinds if applicable:
/kind api-change
/kind deprecation
/kind failing-test
/kind flake
/kind regression
-->

#### What this PR does / why we need it:
[Doc] Add Service Azure LB IPv4 & IPv6 to LB doc
#### Which issue(s) this PR fixes:
<!--
*Automatically closes linked issue when PR is merged.
Usage: `Fixes #<issue number>`, or `Fixes (paste link of issue)`.
_If PR is about `failing-tests or flakes`, please post the related issues/tests in a comment and do not use `Fixes`_*
-->
Fixes #

#### Special notes for your reviewer:

#### Does this PR introduce a user-facing change?
<!--
If no, just write "NONE" in the release-note block below.
If yes, a release note is required:
Enter your extended release note in the block below. If the PR requires additional action from users switching to the new release, include the string "action required".

For more information on release notes see: https://git.k8s.io/community/contributors/guide/release-notes.md
-->
```release-note
NONE
```

#### Additional documentation e.g., KEPs (Kubernetes Enhancement Proposals), usage docs, etc.:

<!--
This section can be blank if this pull request does not require a release note.

When adding links which point to resources within git repositories, like
KEPs or supporting documentation, please reference a specific commit and avoid
linking directly to the master branch. This ensures that links reference a
specific point in time, rather than a document that may change over time.

See here for guidance on getting permanent links to files: https://help.github.com/en/articles/getting-permanent-links-to-files

Please use the following format for linking documentation:
- [KEP]: <link>
- [Usage]: <link>
- [Other doc]: <link>
-->
```docs

```


netlify[bot]: ### <span aria-hidden="true">👷</span> Deploy Preview for *kubernetes-sigs-cloud-provide-azure* processing.


|  Name | Link |
|---------------------------------|------------------------|
|<span aria-hidden="true">🔨</span> Latest commit | ec1c257b1fffcb7b7e35ae3dda5e073ea580c9ad |
|<span aria-hidden="true">🔍</span> Latest deploy log | https://app.netlify.com/sites/kubernetes-sigs-cloud-provide-azure/deploys/63b4e7923282f50009b986a0 |
k8s-ci-robot: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/kubernetes-sigs/cloud-provider-azure/pull/3019#" title="Author self-approved">lzhecheng</a>*

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=kubernetes-sigs%2Fcloud-provider-azure).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- ~~[OWNERS](https://github.com/kubernetes-sigs/cloud-provider-azure/blob/master/OWNERS)~~ [lzhecheng]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->

Patch:
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
index 0627239b8..54af4cb9a 100644
--- a/.github/workflows/release.yaml
+++ b/.github/workflows/release.yaml
@@ -171,7 +171,7 @@ jobs:
           else
             START_TAG=v${VERSION_MAJOR}.${VERSION_MINOR}.$((VERSION_PATCH-1))
           fi
-          START_SHA=$(git rev-parse ${START_TAG})
+          START_SHA=$(git rev-parse ${START_TAG}) 
           END_TAG=${GITHUB_REF_NAME}
           END_SHA=$(git rev-parse ${END_TAG})
 
@@ -180,8 +180,8 @@ jobs:
           echo "END_TAG=${END_TAG} END_SHA=${END_SHA}"
 
           GO111MODULE=on go install k8s.io/release/cmd/release-notes@latest
-
-
+          
+          
           release-notes --repo=cloud-provider-azure \
           	--org=kubernetes-sigs \
           	--branch=${BRANCH} \
@@ -190,7 +190,7 @@ jobs:
           	--markdown-links=true \
           	--required-author='' \
           	--output=release-notes.md
-
+          
           # Generate release note for site
           # Prepend site header
           SITE_RELEASE_NOTE=site/content/en/blog/releases/${{github.ref_name}}.md
@@ -205,7 +205,6 @@ jobs:
         uses: softprops/action-gh-release@v1
         with:
           body_path: release-notes.md
-          draft: true
           files: |
             ./artifacts/cloud-node-manager-*-*/*
             ./artifacts/cloud-controller-manager-*-*/*
@@ -225,13 +224,13 @@ jobs:
             approved
           body: |
             Auto-generated by GitHub Action [Release](https://github.com/${{github.repository}}/actions/runs/${{github.run_id}})
-
+            
             #### What type of PR is this?
             /kind documentation
-
+            
             #### What this PR does / why we need it:
             New release published.
-
+            
             #### Does this PR introduce a user-facing change?
             ```release-note
             None
diff --git a/.pipelines/ccm-image.yml b/.pipelines/ccm-image.yml
index 65745eb07..ffba1d90b 100644
--- a/.pipelines/ccm-image.yml
+++ b/.pipelines/ccm-image.yml
@@ -1,7 +1,7 @@
 steps:
   - task: GoTool@0
     inputs:
-      version: '1.19'
+      version: '1.18.5'
   - bash: |
       git show --stat
       echo $REGISTRY_PASSWORD | docker login $REGISTRY_URL -u $REGISTRY_USERNAME --password-stdin
diff --git a/.pipelines/cnm-image.yml b/.pipelines/cnm-image.yml
index f663d25e6..6fa199ee2 100644
--- a/.pipelines/cnm-image.yml
+++ b/.pipelines/cnm-image.yml
@@ -1,7 +1,7 @@
 steps:
   - task: GoTool@0
     inputs:
-      version: '1.19'
+      version: '1.18.5'
   - bash: |
       git show --stat
       echo $REGISTRY_PASSWORD | docker login $REGISTRY_URL -u $REGISTRY_USERNAME --password-stdin
diff --git a/go.mod b/go.mod
index e67ec1b15..4b3dbc278 100644
--- a/go.mod
+++ b/go.mod
@@ -20,7 +20,7 @@ require (
 	github.com/stretchr/testify v1.8.1
 	golang.org/x/crypto v0.4.0
 	golang.org/x/sys v0.3.0
-	golang.org/x/text v0.6.0
+	golang.org/x/text v0.5.0
 	k8s.io/api v0.26.0
 	k8s.io/apimachinery v0.26.0
 	k8s.io/apiserver v0.26.0
diff --git a/go.sum b/go.sum
index 392062ef8..1977fba89 100644
--- a/go.sum
+++ b/go.sum
@@ -574,8 +574,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
-golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k=
-golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=
+golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
diff --git a/pkg/provider/config/azure_auth.go b/pkg/auth/azure_auth.go
similarity index 93%
rename from pkg/provider/config/azure_auth.go
rename to pkg/auth/azure_auth.go
index a7ba9ea9b..766c36736 100644
--- a/pkg/provider/config/azure_auth.go
+++ b/pkg/auth/azure_auth.go
@@ -14,19 +14,15 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
-package config
+package auth
 
 import (
 	"crypto/rsa"
 	"crypto/x509"
-	"errors"
 	"fmt"
-	"io"
-	"os"
+	"io/ioutil"
 	"strings"
 
-	"sigs.k8s.io/yaml"
-
 	"github.com/Azure/go-autorest/autorest/adal"
 	"github.com/Azure/go-autorest/autorest/azure"
 
@@ -42,10 +38,6 @@ var (
 	ErrorNoAuth = fmt.Errorf("no credentials provided for Azure cloud provider")
 )
 
-const (
-	maxReadLength = 10 * 1 << 20 // 10MB
-)
-
 // AzureAuthConfig holds auth related part of cloud config
 type AzureAuthConfig struct {
 	// The cloud environment identifier. Takes values from https://github.com/Azure/go-autorest/blob/ec5f4903f77ed9927ac95b19ab8e44ada64c1356/autorest/azure/environments.go#L13
@@ -145,7 +137,7 @@ func GetServicePrincipalToken(config *AzureAuthConfig, env *azure.Environment, r
 
 	if len(config.AADClientCertPath) > 0 && len(config.AADClientCertPassword) > 0 {
 		klog.V(2).Infoln("azure: using jwt client_assertion (client_cert+client_private_key) to retrieve access token")
-		certData, err := os.ReadFile(config.AADClientCertPath)
+		certData, err := ioutil.ReadFile(config.AADClientCertPath)
 		if err != nil {
 			return nil, fmt.Errorf("reading the client certificate from file %s: %w", config.AADClientCertPath, err)
 		}
@@ -256,35 +248,6 @@ func ParseAzureEnvironment(cloudName, resourceManagerEndpoint, identitySystem st
 	return &env, err
 }
 
-// ParseAzureAuthConfig returns a parsed configuration for an Azure cloudprovider config file
-func ParseAzureAuthConfig(configReader io.Reader) (*AzureAuthConfig, *azure.Environment, error) {
-	var config AzureAuthConfig
-
-	if configReader == nil {
-		return nil, nil, errors.New("nil config is provided")
-	}
-
-	limitedReader := &io.LimitedReader{R: configReader, N: maxReadLength}
-	configContents, err := io.ReadAll(limitedReader)
-	if err != nil {
-		return nil, nil, err
-	}
-	if limitedReader.N <= 0 {
-		return nil, nil, errors.New("the read limit is reached")
-	}
-	err = yaml.Unmarshal(configContents, &config)
-	if err != nil {
-		return nil, nil, err
-	}
-
-	environment, err := ParseAzureEnvironment(config.Cloud, config.ResourceManagerEndpoint, config.IdentitySystem)
-	if err != nil {
-		return nil, nil, err
-	}
-
-	return &config, environment, nil
-}
-
 // UsesNetworkResourceInDifferentTenant determines whether the AzureAuthConfig indicates to use network resources in
 // different AAD Tenant than those for the cluster. Return true when NetworkResourceTenantID is specified  and not equal
 // to one defined in global configs
diff --git a/pkg/provider/config/doc.go b/pkg/auth/doc.go
similarity index 88%
rename from pkg/provider/config/doc.go
rename to pkg/auth/doc.go
index 3ad7a5daa..52e1d5da9 100644
--- a/pkg/provider/config/doc.go
+++ b/pkg/auth/doc.go
@@ -15,4 +15,4 @@ limitations under the License.
 */
 
 // Package auth provides a general library to authorize Azure ARM clients.
-package config // import "sigs.k8s.io/cloud-provider-azure/pkg/provider/config"
+package auth // import "sigs.k8s.io/cloud-provider-azure/pkg/auth"
diff --git a/pkg/provider/config/testdata/test.pfx b/pkg/auth/testdata/test.pfx
similarity index 100%
rename from pkg/provider/config/testdata/test.pfx
rename to pkg/auth/testdata/test.pfx
diff --git a/pkg/azureclients/v2/go.mod b/pkg/azureclients/v2/go.mod
index 6411ea6e0..3688e5cc5 100644
--- a/pkg/azureclients/v2/go.mod
+++ b/pkg/azureclients/v2/go.mod
@@ -4,17 +4,13 @@ go 1.19
 
 require (
 	github.com/Azure/azure-sdk-for-go/sdk/azcore v1.2.0
-	github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.0
 	github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4 v4.0.0
 	github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice/v2 v2.2.0
 	github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v2 v2.1.0
 	github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.0.0
 	github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.0.0
 	github.com/golang/mock v1.6.0
-	github.com/google/go-cmp v0.5.9
-	github.com/google/uuid v1.3.0
 	github.com/spf13/cobra v1.6.1
-	gopkg.in/dnaeon/go-vcr.v2 v2.3.0
 	k8s.io/client-go v0.26.0
 	k8s.io/klog/v2 v2.80.1
 	sigs.k8s.io/cloud-provider-azure v1.26.1
@@ -32,20 +28,17 @@ require (
 	github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect
 	github.com/Azure/go-autorest/logger v0.2.1 // indirect
 	github.com/Azure/go-autorest/tracing v0.6.0 // indirect
-	github.com/AzureAD/microsoft-authentication-library-for-go v0.7.0 // indirect
 	github.com/fatih/color v1.13.0 // indirect
 	github.com/go-logr/logr v1.2.3 // indirect
 	github.com/gogo/protobuf v1.3.2 // indirect
-	github.com/golang-jwt/jwt/v4 v4.4.2 // indirect
+	github.com/golang-jwt/jwt/v4 v4.2.0 // indirect
 	github.com/google/gofuzz v1.1.0 // indirect
 	github.com/inconshreveable/mousetrap v1.0.1 // indirect
 	github.com/json-iterator/go v1.1.12 // indirect
-	github.com/kylelemons/godebug v1.1.0 // indirect
 	github.com/mattn/go-colorable v0.1.9 // indirect
 	github.com/mattn/go-isatty v0.0.14 // indirect
 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
 	github.com/modern-go/reflect2 v1.0.2 // indirect
-	github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4 // indirect
 	github.com/spf13/pflag v1.0.5 // indirect
 	golang.org/x/crypto v0.4.0 // indirect
 	golang.org/x/mod v0.7.0 // indirect
@@ -56,7 +49,6 @@ require (
 	golang.org/x/tools v0.4.0 // indirect
 	gopkg.in/inf.v0 v0.9.1 // indirect
 	gopkg.in/yaml.v2 v2.4.0 // indirect
-	gopkg.in/yaml.v3 v3.0.1 // indirect
 	k8s.io/api v0.26.0 // indirect
 	k8s.io/apimachinery v0.26.0 // indirect
 	k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 // indirect
diff --git a/pkg/azureclients/v2/go.sum b/pkg/azureclients/v2/go.sum
index c771ae25f..4373a7112 100644
--- a/pkg/azureclients/v2/go.sum
+++ b/pkg/azureclients/v2/go.sum
@@ -2,8 +2,7 @@ github.com/Azure/azure-sdk-for-go v67.2.0+incompatible h1:Uu/Ww6ernvPTrpq31kITVT
 github.com/Azure/azure-sdk-for-go v67.2.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
 github.com/Azure/azure-sdk-for-go/sdk/azcore v1.2.0 h1:sVW/AFBTGyJxDaMYlq0ct3jUXTtj12tQ6zE2GZUgVQw=
 github.com/Azure/azure-sdk-for-go/sdk/azcore v1.2.0/go.mod h1:uGG2W01BaETf0Ozp+QxxKJdMBNRWPdstHG0Fmdwn1/U=
-github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.0 h1:t/W5MYAuQy81cvM8VUNfRLzhtKpXhVUAN7Cd7KVbTyc=
-github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.0/go.mod h1:NBanQUfSWiWn3QEpWDTCU0IjBECKOYvl2R8xdRtMtiM=
+github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.1.0 h1:QkAcEIAKbNL4KoFr4SathZPhDhF4mVwpBMFlYjyAqy8=
 github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0 h1:jp0dGvZ7ZK0mgqnTSClMxa5xuRL7NZgHameVYF6BurY=
 github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v3 v3.0.1 h1:H3g2mkmu105ON0c/Gqx3Bm+bzoIijLom8LmV9Gjn7X0=
@@ -39,8 +38,7 @@ github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+Z
 github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
 github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo=
 github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
-github.com/AzureAD/microsoft-authentication-library-for-go v0.7.0 h1:VgSJlZH5u0k2qxSpqyghcFQKmvYckj46uymKK5XzkBM=
-github.com/AzureAD/microsoft-authentication-library-for-go v0.7.0/go.mod h1:BDJ5qMFKx9DugEg3+uQSDCdbYPr5s9vBTrL9P8TpqOU=
+github.com/AzureAD/microsoft-authentication-library-for-go v0.5.1 h1:BWe8a+f/t+7KY7zH2mqygeUD0t8hNFXe08p1Pb3/jKE=
 github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
@@ -54,19 +52,17 @@ github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
 github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
 github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
 github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
+github.com/golang-jwt/jwt v3.2.1+incompatible h1:73Z+4BJcrTC+KczS6WvTPvRGOp1WmfEP4Q1lOd9Z/+c=
 github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
+github.com/golang-jwt/jwt/v4 v4.2.0 h1:besgBTC8w8HjP6NzQdxwKH9Z5oQMZ24ThTrHp3cZ8eU=
 github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
-github.com/golang-jwt/jwt/v4 v4.4.2 h1:rcc4lwaZgFMCZ5jxF9ABolDcIHdBytAFgqFPbSJQAYs=
-github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
 github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
 github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
 github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
-github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
 github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
-github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
-github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
 github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc=
 github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
 github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
@@ -75,7 +71,6 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI
 github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
 github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
 github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
-github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
 github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U=
 github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
 github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
@@ -91,7 +86,6 @@ github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
 github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
 github.com/onsi/gomega v1.24.2 h1:J/tulyYK6JwBldPViHJReihxxZ+22FHs0piGjQAvoUE=
 github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4 h1:Qj1ukM4GlMWXNdMBuXcXfz/Kw9s1qm0CLY32QxuSImI=
-github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
@@ -165,8 +159,6 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
-gopkg.in/dnaeon/go-vcr.v2 v2.3.0 h1:nwyjLPYlDmZkurnsEr5iWdjqy8kM+xV80E3TbvTA4Ow=
-gopkg.in/dnaeon/go-vcr.v2 v2.3.0/go.mod h1:OgKb3ClaX2nN64BtvDFed3NIIEbB4jx1augFJq+IiYo=
 gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
 gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
diff --git a/pkg/azureclients/v2/recording/counter.go b/pkg/azureclients/v2/recording/counter.go
deleted file mode 100644
index 8b997c678..000000000
--- a/pkg/azureclients/v2/recording/counter.go
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
-Copyright 2022 The Kubernetes Authors.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package recording
-
-import (
-	"fmt"
-	"net/http"
-	"sync"
-)
-
-var requestHeadersToRemove = []string{
-	// remove all Authorization headers from stored requests
-	"Authorization",
-
-	// Not needed, adds to diff churn:
-	"User-Agent",
-}
-
-var responseHeadersToRemove = []string{
-	// Request IDs
-	"X-Ms-Arm-Service-Request-Id",
-	"X-Ms-Correlation-Request-Id",
-	"X-Ms-Request-Id",
-	"X-Ms-Routing-Request-Id",
-	"X-Ms-Client-Request-Id",
-	"Client-Request-Id",
-
-	// Quota limits
-	"X-Ms-Ratelimit-Remaining-Subscription-Deletes",
-	"X-Ms-Ratelimit-Remaining-Subscription-Reads",
-	"X-Ms-Ratelimit-Remaining-Subscription-Writes",
-
-	// Not needed, adds to diff churn
-	"Date",
-
-	// Causes client to delay long time
-	"Retry-After",
-}
-
-// Wraps an inner HTTP roundtripper to add a
-// counter for duplicated request URIs. This
-// is then used to match up requests in the recorder
-// - it is needed as we have multiple requests with
-// the same Request URL and it will return the first
-// one that matches.
-type requestCounter struct {
-	inner http.RoundTripper
-
-	countsMutex sync.Mutex
-	counts      map[string]uint32
-}
-
-var _ http.RoundTripper = &requestCounter{}
-
-func addCountHeader(inner http.RoundTripper) *requestCounter {
-	return &requestCounter{
-		inner:       inner,
-		counts:      make(map[string]uint32),
-		countsMutex: sync.Mutex{},
-	}
-}
-
-const countHeader = "TEST-REQUEST-ATTEMPT"
-
-func (rt *requestCounter) RoundTrip(req *http.Request) (*http.Response, error) {
-	key := req.Method + ":" + req.URL.String()
-	rt.countsMutex.Lock()
-	count := rt.counts[key]
-	rt.counts[key] = count + 1
-	rt.countsMutex.Unlock()
-	req.Header.Set(countHeader, fmt.Sprintf("%d", count))
-	return rt.inner.RoundTrip(req)
-}
diff --git a/pkg/azureclients/v2/recording/recorder.go b/pkg/azureclients/v2/recording/recorder.go
deleted file mode 100644
index 206aa99f6..000000000
--- a/pkg/azureclients/v2/recording/recorder.go
+++ /dev/null
@@ -1,218 +0,0 @@
-/*
-Copyright 2022 The Kubernetes Authors.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package recording
-
-import (
-	"bytes"
-	"errors"
-	"io"
-	"net/http"
-	"os"
-	"regexp"
-	"strings"
-
-	"github.com/Azure/azure-sdk-for-go/sdk/azcore"
-	"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
-	"github.com/google/uuid"
-	"gopkg.in/dnaeon/go-vcr.v2/cassette"
-	"gopkg.in/dnaeon/go-vcr.v2/recorder"
-)
-
-var (
-	dateMatcher   = regexp.MustCompile(`\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(.\d+)?Z`)
-	sshKeyMatcher = regexp.MustCompile("ssh-rsa [0-9a-zA-Z+/=]+")
-
-	// This is pretty involved, here's the breakdown of what each bit means:
-	// [p|P]assword":\s*" - find any JSON field that ends in the string password, followed by any number of spaces and another quote.
-	// ((?:[^\\"]*?(?:(\\\\)|(\\"))*?)*?) - The outer group is a capturing group, which selects the actual password
-	// [^\\"]*? - this matches any characters that aren't \ or " (need to handle them specially because of escaped quotes)
-	// (?:(?:\\\\)|(?:\\")|(?:\\))*? - lazily match any number of escaped backslahes or escaped quotes.
-	// The above two sections are repeated until the first unescaped "s
-	passwordMatcher = regexp.MustCompile(`[p|P]assword":\s*"((?:[^\\"]*?(?:(?:\\\\)|(?:\\")|(?:\\))*?)*?)"`)
-
-	// keyMatcher matches any valid base64 value with at least 10 sets of 4 bytes of data that ends in = or ==.
-	// Both storage account keys and Redis account keys are longer than that and end in = or ==. Note that technically
-	// base64 values need not end in == or =, but allowing for that in the match will flag tons of false positives as
-	// any text (including long URLs) have strings of characters that meet this requirement. There are other base64 values
-	// in the payloads (such as operationResults URLs for polling async operations for some services) that seem to use
-	// very long base64 strings as well.
-	keyMatcher = regexp.MustCompile("(?:[A-Za-z0-9+/]{4}){10,}(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)")
-)
-
-// hideDates replaces all ISO8601 datetimes with a fixed value
-// this lets us match requests that may contain time-sensitive information (timestamps, etc)
-func hideDates(s string) string {
-	return dateMatcher.ReplaceAllLiteralString(s, "2001-02-03T04:05:06Z") // this should be recognizable/parseable as a fake date
-}
-
-// hideSSHKeys hides anything that looks like SSH keys
-func hideSSHKeys(s string) string {
-	return sshKeyMatcher.ReplaceAllLiteralString(s, "ssh-rsa {KEY}")
-}
-
-// hidePasswords hides anything that looks like a generated password
-func hidePasswords(s string) string {
-	matches := passwordMatcher.FindAllStringSubmatch(s, -1)
-	for _, match := range matches {
-		for n, submatch := range match {
-			if n%2 == 0 {
-				continue
-			}
-			s = strings.ReplaceAll(s, submatch, "{PASSWORD}")
-		}
-	}
-	return s
-}
-
-func hideKeys(s string) string {
-	return keyMatcher.ReplaceAllLiteralString(s, "{KEY}")
-}
-
-func hideRecordingData(s string) string {
-	result := hideDates(s)
-	result = hideSSHKeys(result)
-	result = hidePasswords(result)
-	result = hideKeys(result)
-
-	return result
-}
-
-type Recorder struct {
-	cassetteName   string
-	credential     azcore.TokenCredential
-	rec            *recorder.Recorder
-	subscriptionID string
-
-	httpClient *http.Client
-}
-
-func NewRecorder(cassetteName string) (*Recorder, error) {
-	rec, err := recorder.New(cassetteName)
-	if err != nil {
-		return nil, err
-	}
-	rec.SkipRequestLatency = true
-
-	var tokenCredential azcore.TokenCredential
-	var subscriptionID string
-
-	if rec.Mode() == recorder.ModeRecording ||
-		rec.Mode() == recorder.ModeDisabled {
-		tokenCredential, err = azidentity.NewDefaultAzureCredential(nil)
-		if err != nil {
-			return nil, err
-		}
-
-		subscriptionID = os.Getenv("AZURE_SUBSCRIPTION_ID")
-		if subscriptionID == "" {
-			return nil, errors.New("required environment variable AZURE_SUBSCRIPTION_ID was not supplied")
-		}
-	} else {
-		// if we are replaying, we won't need auth
-		// and we use a dummy subscription ID
-		subscriptionID = uuid.Nil.String()
-		tokenCredential = nil
-	}
-
-	// check body as well as URL/Method (copied from go-vcr documentation)
-	rec.SetMatcher(func(r *http.Request, i cassette.Request) bool {
-		if !cassette.DefaultMatcher(r, i) {
-			return false
-		}
-
-		// verify custom request count header (see counting_roundtripper.go)
-		if r.Header.Get(countHeader) != i.Headers.Get(countHeader) {
-			return false
-		}
-
-		if r.Body == nil {
-			return i.Body == ""
-		}
-
-		var b bytes.Buffer
-		if _, err := b.ReadFrom(r.Body); err != nil {
-			panic(err)
-		}
-
-		r.Body = io.NopCloser(&b)
-		return b.String() == "" || hideRecordingData(b.String()) == i.Body
-	})
-
-	rec.AddSaveFilter(func(i *cassette.Interaction) error {
-		// rewrite all request/response fields to hide the real subscription ID
-		// this is *not* a security measure but intended to make the tests updateable from
-		// any subscription, so a contributor can update the tests against their own sub.
-		hideSubID := func(s string) string {
-			return strings.ReplaceAll(s, subscriptionID, uuid.Nil.String())
-		}
-
-		i.Request.Body = hideRecordingData(hideSubID(i.Request.Body))
-		i.Response.Body = hideRecordingData(hideSubID(i.Response.Body))
-		i.Request.URL = hideSubID(i.Request.URL)
-
-		for _, values := range i.Request.Headers {
-			for i := range values {
-				values[i] = hideSubID(values[i])
-			}
-		}
-
-		for _, values := range i.Response.Headers {
-			for i := range values {
-				values[i] = hideSubID(values[i])
-			}
-		}
-
-		for _, header := range requestHeadersToRemove {
-			delete(i.Request.Headers, header)
-		}
-
-		for _, header := range responseHeadersToRemove {
-			delete(i.Response.Headers, header)
-		}
-
-		return nil
-	})
-
-	return &Recorder{
-		cassetteName:   cassetteName,
-		credential:     tokenCredential,
-		rec:            rec,
-		subscriptionID: subscriptionID,
-	}, nil
-}
-
-func (r *Recorder) HTTPClient() *http.Client {
-	if r.httpClient == nil {
-		r.httpClient = &http.Client{
-			Transport: addCountHeader(translateErrors(r.rec, r.cassetteName)),
-		}
-	}
-
-	return r.httpClient
-}
-
-func (r *Recorder) TokenCredential() azcore.TokenCredential {
-	return r.credential
-}
-
-func (r *Recorder) SubscriptionID() string {
-	return r.subscriptionID
-}
-
-func (r *Recorder) Stop() error {
-	return r.rec.Stop()
-}
diff --git a/pkg/azureclients/v2/recording/transport.go b/pkg/azureclients/v2/recording/transport.go
deleted file mode 100644
index 131877515..000000000
--- a/pkg/azureclients/v2/recording/transport.go
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
-Copyright 2022 The Kubernetes Authors.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package recording
-
-import (
-	"fmt"
-	"io"
-	"net/http"
-	"strings"
-
-	"github.com/google/go-cmp/cmp"
-	"gopkg.in/dnaeon/go-vcr.v2/cassette"
-	"gopkg.in/dnaeon/go-vcr.v2/recorder"
-)
-
-func translateErrors(r *recorder.Recorder, cassetteName string) http.RoundTripper {
-	return errorTranslation{r, cassetteName, nil}
-}
-
-type errorTranslation struct {
-	recorder     *recorder.Recorder
-	cassetteName string
-
-	cassette *cassette.Cassette
-}
-
-func (w errorTranslation) ensureCassette() *cassette.Cassette {
-	if w.cassette == nil {
-		cassette, err := cassette.Load(w.cassetteName)
-		if err != nil {
-			panic(fmt.Sprintf("unable to load cassette %q", w.cassetteName))
-		}
-
-		w.cassette = cassette
-	}
-
-	return w.cassette
-}
-
-func (w errorTranslation) RoundTrip(req *http.Request) (*http.Response, error) {
-	resp, originalErr := w.recorder.RoundTrip(req)
-	// sorry, go-vcr doesn't expose the error type or message
-	if originalErr == nil || !strings.Contains(originalErr.Error(), "interaction not found") {
-		return resp, originalErr
-	}
-
-	sentBodyString := "<nil>"
-	if req.Body != nil {
-		bodyBytes, bodyErr := io.ReadAll(req.Body)
-		if bodyErr != nil {
-			// see invocation of SetMatcher in the createRecorder, which does this
-			panic("io.ReadAll(req.Body) failed, this should always succeed because req.Body has been replaced by a buffer")
-		}
-
-		// Apply the same body filtering that we do in recordings so that the diffs don't show things
-		// that we've just removed
-		sentBodyString = hideRecordingData(string(bodyBytes))
-	}
-
-	// find all request bodies for the specified method/URL combination
-	matchingBodies := w.findMatchingBodies(req)
-
-	if len(matchingBodies) == 0 {
-		return nil, fmt.Errorf("cannot find go-vcr recording for request (cassette: %q) (no responses recorded for this method/URL): %s %s (attempt: %s)\n\n",
-			w.cassetteName,
-			req.Method,
-			req.URL.String(),
-			req.Header.Get(countHeader))
-	}
-
-	// locate the request body with the shortest diff from the sent body
-	shortestDiff := ""
-	for i, bodyString := range matchingBodies {
-		diff := cmp.Diff(bodyString, sentBodyString)
-		if i == 0 || len(diff) < len(shortestDiff) {
-			shortestDiff = diff
-		}
-	}
-
-	return nil, fmt.Errorf("cannot find go-vcr recording for request (cassette: %q) (body mismatch): %s %s\nShortest body diff: %s\n\n",
-		w.cassetteName,
-		req.Method,
-		req.URL.String(),
-		shortestDiff)
-}
-
-// finds bodies for interactions where request method, URL, and countHeader match
-func (w errorTranslation) findMatchingBodies(r *http.Request) []string {
-	urlString := r.URL.String()
-	var result []string
-	for _, interaction := range w.ensureCassette().Interactions {
-		if urlString == interaction.URL && r.Method == interaction.Request.Method &&
-			r.Header.Get(countHeader) == interaction.Request.Headers.Get(countHeader) {
-			result = append(result, interaction.Request.Body)
-		}
-	}
-
-	return result
-}
diff --git a/pkg/credentialprovider/azure_acr_helper.go b/pkg/credentialprovider/azure_acr_helper.go
index 6a2221686..4fdf3a70a 100644
--- a/pkg/credentialprovider/azure_acr_helper.go
+++ b/pkg/credentialprovider/azure_acr_helper.go
@@ -50,6 +50,7 @@ import (
 	"errors"
 	"fmt"
 	"io"
+	"io/ioutil"
 	"net/http"
 	"net/url"
 	"strconv"
@@ -181,7 +182,7 @@ func performTokenExchange(
 
 	var content []byte
 	limitedReader := &io.LimitedReader{R: exchange.Body, N: maxReadLength}
-	if content, err = io.ReadAll(limitedReader); err != nil {
+	if content, err = ioutil.ReadAll(limitedReader); err != nil {
 		return "", fmt.Errorf("Www-Authenticate: error reading response from %s", authEndpoint)
 	}
 
diff --git a/pkg/credentialprovider/azure_credentials.go b/pkg/credentialprovider/azure_credentials.go
index c051560fc..7e4b7475e 100644
--- a/pkg/credentialprovider/azure_credentials.go
+++ b/pkg/credentialprovider/azure_credentials.go
@@ -21,18 +21,20 @@ import (
 	"errors"
 	"fmt"
 	"io"
+	"io/ioutil"
 	"os"
 	"regexp"
 	"strings"
 	"time"
 
-	providerconfig "sigs.k8s.io/cloud-provider-azure/pkg/provider/config"
-
 	"github.com/Azure/go-autorest/autorest/adal"
 	"github.com/Azure/go-autorest/autorest/azure"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/klog/v2"
 	"k8s.io/kubelet/pkg/apis/credentialprovider/v1alpha1"
+	"sigs.k8s.io/yaml"
+
+	"sigs.k8s.io/cloud-provider-azure/pkg/auth"
 )
 
 // Refer: https://github.com/kubernetes/kubernetes/blob/master/pkg/credentialprovider/azure/azure_credentials.go
@@ -55,7 +57,7 @@ type CredentialProvider interface {
 
 // acrProvider implements the credential provider interface for Azure Container Registry.
 type acrProvider struct {
-	config                *providerconfig.AzureAuthConfig
+	config                *auth.AzureAuthConfig
 	environment           *azure.Environment
 	servicePrincipalToken *adal.ServicePrincipalToken
 }
@@ -76,12 +78,12 @@ func NewAcrProvider(configFile string) (CredentialProvider, error) {
 }
 
 func newAcrProviderFromConfigReader(configReader io.Reader) (*acrProvider, error) {
-	config, env, err := providerconfig.ParseAzureAuthConfig(configReader)
+	config, env, err := parseConfig(configReader)
 	if err != nil {
 		return nil, fmt.Errorf("failed to load config: %w", err)
 	}
 
-	servicePrincipalToken, err := providerconfig.GetServicePrincipalToken(config, env, env.ServiceManagementEndpoint)
+	servicePrincipalToken, err := auth.GetServicePrincipalToken(config, env, env.ServiceManagementEndpoint)
 	if err != nil {
 		return nil, fmt.Errorf("failed to create service principal token: %w", err)
 	}
@@ -162,6 +164,35 @@ func (a *acrProvider) GetCredentials(ctx context.Context, image string, args []s
 	return response, nil
 }
 
+// ParseConfig returns a parsed configuration for an Azure cloudprovider config file
+func parseConfig(configReader io.Reader) (*auth.AzureAuthConfig, *azure.Environment, error) {
+	var config auth.AzureAuthConfig
+
+	if configReader == nil {
+		return nil, nil, errors.New("nil config is provided")
+	}
+
+	limitedReader := &io.LimitedReader{R: configReader, N: maxReadLength}
+	configContents, err := ioutil.ReadAll(limitedReader)
+	if err != nil {
+		return nil, nil, err
+	}
+	if limitedReader.N <= 0 {
+		return nil, nil, errors.New("the read limit is reached")
+	}
+	err = yaml.Unmarshal(configContents, &config)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	environment, err := auth.ParseAzureEnvironment(config.Cloud, config.ResourceManagerEndpoint, config.IdentitySystem)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	return &config, environment, nil
+}
+
 // getFromACR gets credentials from ACR.
 func (a *acrProvider) getFromACR(loginServer string) (string, string, error) {
 	// Run EnsureFresh to make sure the token is valid and does not expire
diff --git a/pkg/provider/azure.go b/pkg/provider/azure.go
index 2047d7da9..da736e55a 100644
--- a/pkg/provider/azure.go
+++ b/pkg/provider/azure.go
@@ -48,6 +48,7 @@ import (
 	cloudprovider "k8s.io/cloud-provider"
 	"k8s.io/klog/v2"
 
+	"sigs.k8s.io/cloud-provider-azure/pkg/auth"
 	azclients "sigs.k8s.io/cloud-provider-azure/pkg/azureclients"
 	"sigs.k8s.io/cloud-provider-azure/pkg/azureclients/blobclient"
 	"sigs.k8s.io/cloud-provider-azure/pkg/azureclients/containerserviceclient"
@@ -104,7 +105,7 @@ var (
 // See https://kubernetes.io/docs/reference/using-api/deprecation-policy/#deprecating-a-flag-or-cli
 // for more details.
 type Config struct {
-	ratelimitconfig.AzureAuthConfig
+	auth.AzureAuthConfig
 	ratelimitconfig.CloudProviderRateLimitConfig
 
 	// The cloud configure type for Azure cloud provider. Supported values are file, secret and merge.
@@ -547,13 +548,13 @@ func (az *Cloud) InitializeCloudFromConfig(ctx context.Context, config *Config,
 		}
 	}
 
-	env, err := ratelimitconfig.ParseAzureEnvironment(config.Cloud, config.ResourceManagerEndpoint, config.IdentitySystem)
+	env, err := auth.ParseAzureEnvironment(config.Cloud, config.ResourceManagerEndpoint, config.IdentitySystem)
 	if err != nil {
 		return err
 	}
 
-	servicePrincipalToken, err := ratelimitconfig.GetServicePrincipalToken(&config.AzureAuthConfig, env, env.ServiceManagementEndpoint)
-	if errors.Is(err, ratelimitconfig.ErrorNoAuth) {
+	servicePrincipalToken, err := auth.GetServicePrincipalToken(&config.AzureAuthConfig, env, env.ServiceManagementEndpoint)
+	if errors.Is(err, auth.ErrorNoAuth) {
 		// Only controller-manager would lazy-initialize from secret, and credentials are required for such case.
 		if fromSecret {
 			err := fmt.Errorf("no credentials provided for Azure cloud provider")
@@ -732,11 +733,11 @@ func (az *Cloud) configureMultiTenantClients(servicePrincipalToken *adal.Service
 	var multiTenantServicePrincipalToken *adal.MultiTenantServicePrincipalToken
 	var networkResourceServicePrincipalToken *adal.ServicePrincipalToken
 	if az.Config.UsesNetworkResourceInDifferentTenant() {
-		multiTenantServicePrincipalToken, err = ratelimitconfig.GetMultiTenantServicePrincipalToken(&az.Config.AzureAuthConfig, &az.Environment)
+		multiTenantServicePrincipalToken, err = auth.GetMultiTenantServicePrincipalToken(&az.Config.AzureAuthConfig, &az.Environment)
 		if err != nil {
 			return err
 		}
-		networkResourceServicePrincipalToken, err = ratelimitconfig.GetNetworkResourceServicePrincipalToken(&az.Config.AzureAuthConfig, &az.Environment)
+		networkResourceServicePrincipalToken, err = auth.GetNetworkResourceServicePrincipalToken(&az.Config.AzureAuthConfig, &az.Environment)
 		if err != nil {
 			return err
 		}
diff --git a/pkg/provider/azure_fakes.go b/pkg/provider/azure_fakes.go
index f74960185..128cab6cc 100644
--- a/pkg/provider/azure_fakes.go
+++ b/pkg/provider/azure_fakes.go
@@ -20,13 +20,12 @@ import (
 	"context"
 	"fmt"
 
-	"sigs.k8s.io/cloud-provider-azure/pkg/provider/config"
-
 	"github.com/golang/mock/gomock"
 
 	"k8s.io/apimachinery/pkg/util/sets"
 	"k8s.io/client-go/tools/record"
 
+	"sigs.k8s.io/cloud-provider-azure/pkg/auth"
 	"sigs.k8s.io/cloud-provider-azure/pkg/azureclients/diskclient/mockdiskclient"
 	"sigs.k8s.io/cloud-provider-azure/pkg/azureclients/interfaceclient/mockinterfaceclient"
 	"sigs.k8s.io/cloud-provider-azure/pkg/azureclients/loadbalancerclient/mockloadbalancerclient"
@@ -76,7 +75,7 @@ func NewTestFlexScaleSet(ctrl *gomock.Controller) (*FlexScaleSet, error) {
 func GetTestCloud(ctrl *gomock.Controller) (az *Cloud) {
 	az = &Cloud{
 		Config: Config{
-			AzureAuthConfig: config.AzureAuthConfig{
+			AzureAuthConfig: auth.AzureAuthConfig{
 				TenantID:       "tenant",
 				SubscriptionID: "subscription",
 			},
diff --git a/site/content/en/topics/loadbalancer.md b/site/content/en/topics/loadbalancer.md
index bb881f959..b48350b63 100644
--- a/site/content/en/topics/loadbalancer.md
+++ b/site/content/en/topics/loadbalancer.md
@@ -32,6 +32,8 @@ Below is a list of annotations supported for Kubernetes services with type `Load
 | `service.beta.kubernetes.io/azure-load-balancer-health-probe-interval` | Health probe interval | Refer to the detailed docs [here](#custom-load-balancer-health-probe) | v1.21 and later  with out-of-tree cloud provider  |
 | `service.beta.kubernetes.io/azure-load-balancer-health-probe-num-of-probe` | The minimum number of unhealthy responses of health probe  |  Refer to the detailed docs [here](#custom-load-balancer-health-probe) |	v1.21 and later  with out-of-tree cloud provider|
 | `service.beta.kubernetes.io/azure-load-balancer-health-probe-request-path` | Request path of the health probe | Refer to the detailed docs [here](#custom-load-balancer-health-probe) | v1.20 and later  with out-of-tree cloud provider|
+| `service.beta.kubernetes.io/azure-load-balancer-ipv4` | Load balancer IPv4 address | Specify the load balancer IP of IPv4, deprecating Service.spec.loadBalancerIP | v1.21 and later |
+| `service.beta.kubernetes.io/azure-load-balancer-ipv6` | Load balancer IPv6 address | Specify the load balancer IP of IPv6, deprecating Service.spec.loadBalancerIP | v1.21 and later |
 | `service.beta.kubernetes.io/port_{port}_no_lb_rule` | true/false |  {port} is the port number in the service. When it is set to true, no lb rule and health probe rule for this port will be generated. health check service should not be exposed to the public internet(e.g. istio/envoy health check service) | v1.24 and later with out-of-tree cloud provider|
 | `service.beta.kubernetes.io/port_{port}_no_probe_rule` | true/false |  {port} is the port number in the service. When it is set to true, no health probe rule for this port will be generated. | v1.24 and later with out-of-tree cloud provider|
 | `service.beta.kubernetes.io/port_{port}_health-probe_protocol` | Health probe protocol | {port} is the port number in the service. Explicit protocol for the health probe for the service port {port}, overriding port.appProtocol if set.  Refer to the detailed docs [here](#custom-load-balancer-health-probe) | v1.21 and later with out-of-tree cloud provider|
@@ -48,6 +50,13 @@ Please note that
 
 * When `loadBalancerSourceRanges` have been set on service spec, `service.beta.kubernetes.io/azure-allowed-service-tags` won't work because of DROP iptables rules from kube-proxy. The CIDRs from service tags should be merged into `loadBalancerSourceRanges` to make it work.
 
+### Setting LoadBalaner IP
+
+If you want to specify an IP address for the load balancer, there are two ways:
+
+* Recommended: Set Service annotations `service.beta.kubernetes.io/azure-load-balancer-ipv4` for an IPv4 address and `service.beta.kubernetes.io/azure-load-balancer-ipv6` for an IPv6 address. Dual-stack support will be implemented soon. It is highly recommended for new Services.
+* Deprecating: Set Service field: `Service.Spec.LoadbalancerIP`. This field is deprecating following [upstream kubernetes](https://github.com/kubernetes/kubernetes/pull/107235) and it cannot support dual-stack. However, current usage remains the same and existing Services are expected to work without modification.
+
 ### Load balancer selection modes
 
 There are currently three possible load balancer selection modes :
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 9bdd5725b..09e8a8cb3 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -406,7 +406,7 @@ golang.org/x/sys/windows/svc
 # golang.org/x/term v0.3.0
 ## explicit; go 1.17
 golang.org/x/term
-# golang.org/x/text v0.6.0
+# golang.org/x/text v0.5.0
 ## explicit; go 1.17
 golang.org/x/text/cases
 golang.org/x/text/encoding


Test patch:
diff --git a/.pipelines/run-autoscaling-e2e.yml b/.pipelines/run-autoscaling-e2e.yml
index 51c786281..138898a1e 100644
--- a/.pipelines/run-autoscaling-e2e.yml
+++ b/.pipelines/run-autoscaling-e2e.yml
@@ -33,7 +33,7 @@ stages:
       steps:
         - task: GoTool@0
           inputs:
-            version: '1.19'
+            version: '1.18'
         - bash: |
             CLUSTER_TYPE="autoscaling"
             echo "##vso[task.setvariable variable=CLUSTER_TYPE]${CLUSTER_TYPE}"
diff --git a/.pipelines/run-autoscaling-multipool-e2e.yml b/.pipelines/run-autoscaling-multipool-e2e.yml
index fdcb656fb..33f0c1f88 100644
--- a/.pipelines/run-autoscaling-multipool-e2e.yml
+++ b/.pipelines/run-autoscaling-multipool-e2e.yml
@@ -33,7 +33,7 @@ stages:
       steps:
         - task: GoTool@0
           inputs:
-            version: '1.19'
+            version: '1.18'
         - bash: |
             CLUSTER_TYPE="autoscaling-multipool"
             echo "##vso[task.setvariable variable=CLUSTER_TYPE]${CLUSTER_TYPE}"
diff --git a/.pipelines/run-basic-lb-e2e.yml b/.pipelines/run-basic-lb-e2e.yml
index 36fe39c7f..6408690fb 100644
--- a/.pipelines/run-basic-lb-e2e.yml
+++ b/.pipelines/run-basic-lb-e2e.yml
@@ -36,7 +36,7 @@ stages:
       steps:
         - task: GoTool@0
           inputs:
-            version: '1.19'
+            version: '1.18'
         - bash: |
             CLUSTER_TYPE="basic-lb"
             echo "##vso[task.setvariable variable=CLUSTER_TYPE]${CLUSTER_TYPE}"
diff --git a/.pipelines/scripts/run-e2e.sh b/.pipelines/scripts/run-e2e.sh
index e3849c9b5..5bc4ef427 100755
--- a/.pipelines/scripts/run-e2e.sh
+++ b/.pipelines/scripts/run-e2e.sh
@@ -38,12 +38,12 @@ get_k8s_version() {
     return
   fi
   K8S_RELEASE="${K8S_RELEASE:-$(echo ${BUILD_SOURCE_BRANCH_NAME:-} | cut -f2 -d'-')}"
-  AKS_KUBERNETES_VERSION=$(az aks get-versions -l "${AZURE_LOCATION:-}" --subscription ${AZURE_SUBSCRIPTION_ID:-} --output json \
-    | jq -r --arg K8S_RELEASE "${K8S_RELEASE:-}" 'last(.orchestrators |.[] | select(.orchestratorVersion | startswith($K8S_RELEASE))) | .orchestratorVersion')
+  AKS_KUBERNETES_VERSION=$(az aks get-versions -l "${AZURE_LOCATION:-}" --output json \
+    | jq -r --arg K8S_RELEASE "${K8S_RELEASE:-}" '[.orchestrators |.[] | select(.orchestratorVersion | startswith($K8S_RELEASE))][0] | .orchestratorVersion')
   # Normally, K8S_RELEASE has at least one match in AKS, but in case the k8s release is the first minor version,
   # not picked by AKS, we use the latest AKS k8s version as a try-your-best workaround.
   if [[ "${AKS_KUBERNETES_VERSION:-}" == "null" ]]; then
-  AKS_KUBERNETES_VERSION=$(az aks get-versions -l "${AZURE_LOCATION:-}" --subscription ${AZURE_SUBSCRIPTION_ID:-} --output json \
+  AKS_KUBERNETES_VERSION=$(az aks get-versions -l "${AZURE_LOCATION:-}" --output json \
     | jq -r '.orchestrators |.[] |select(.upgrades | .==null) |.orchestratorVersion')
   fi
 }
diff --git a/pkg/provider/config/azure_auth_test.go b/pkg/auth/azure_auth_test.go
similarity index 99%
rename from pkg/provider/config/azure_auth_test.go
rename to pkg/auth/azure_auth_test.go
index b818333c1..47acdaeb4 100644
--- a/pkg/provider/config/azure_auth_test.go
+++ b/pkg/auth/azure_auth_test.go
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
-package config
+package auth
 
 import (
 	"io/ioutil"
diff --git a/pkg/provider/azure_config_test.go b/pkg/provider/azure_config_test.go
index 3f6ddabb0..ac2e2eaa6 100644
--- a/pkg/provider/azure_config_test.go
+++ b/pkg/provider/azure_config_test.go
@@ -20,8 +20,6 @@ import (
 	"context"
 	"testing"
 
-	"sigs.k8s.io/cloud-provider-azure/pkg/provider/config"
-
 	"sigs.k8s.io/cloud-provider-azure/pkg/consts"
 
 	"github.com/golang/mock/gomock"
@@ -36,11 +34,13 @@ import (
 	fakeclient "k8s.io/client-go/kubernetes/fake"
 
 	"sigs.k8s.io/yaml"
+
+	"sigs.k8s.io/cloud-provider-azure/pkg/auth"
 )
 
 func getTestConfig() *Config {
 	return &Config{
-		AzureAuthConfig: config.AzureAuthConfig{
+		AzureAuthConfig: auth.AzureAuthConfig{
 			TenantID:        "TenantID",
 			SubscriptionID:  "SubscriptionID",
 			AADClientID:     "AADClientID",
@@ -61,7 +61,7 @@ func getTestConfig() *Config {
 
 func getTestCloudConfigTypeSecretConfig() *Config {
 	return &Config{
-		AzureAuthConfig: config.AzureAuthConfig{
+		AzureAuthConfig: auth.AzureAuthConfig{
 			TenantID:       "TenantID",
 			SubscriptionID: "SubscriptionID",
 		},
@@ -75,7 +75,7 @@ func getTestCloudConfigTypeSecretConfig() *Config {
 
 func getTestCloudConfigTypeMergeConfig() *Config {
 	return &Config{
-		AzureAuthConfig: config.AzureAuthConfig{
+		AzureAuthConfig: auth.AzureAuthConfig{
 			TenantID:       "TenantID",
 			SubscriptionID: "SubscriptionID",
 		},
diff --git a/pkg/provider/azure_test.go b/pkg/provider/azure_test.go
index a87cf097b..443e6dca6 100644
--- a/pkg/provider/azure_test.go
+++ b/pkg/provider/azure_test.go
@@ -41,6 +41,7 @@ import (
 	cloudproviderapi "k8s.io/cloud-provider/api"
 	servicehelpers "k8s.io/cloud-provider/service/helpers"
 
+	"sigs.k8s.io/cloud-provider-azure/pkg/auth"
 	"sigs.k8s.io/cloud-provider-azure/pkg/azureclients/interfaceclient/mockinterfaceclient"
 	"sigs.k8s.io/cloud-provider-azure/pkg/azureclients/loadbalancerclient/mockloadbalancerclient"
 	"sigs.k8s.io/cloud-provider-azure/pkg/azureclients/privatelinkserviceclient/mockprivatelinkserviceclient"
@@ -50,7 +51,6 @@ import (
 	"sigs.k8s.io/cloud-provider-azure/pkg/azureclients/vmclient/mockvmclient"
 	"sigs.k8s.io/cloud-provider-azure/pkg/azureclients/zoneclient/mockzoneclient"
 	"sigs.k8s.io/cloud-provider-azure/pkg/consts"
-	providerconfig "sigs.k8s.io/cloud-provider-azure/pkg/provider/config"
 	"sigs.k8s.io/cloud-provider-azure/pkg/retry"
 )
 
@@ -3619,7 +3619,7 @@ func TestInitializeCloudFromConfig(t *testing.T) {
 	assert.Equal(t, expectedErr, err)
 
 	config = Config{
-		AzureAuthConfig: providerconfig.AzureAuthConfig{
+		AzureAuthConfig: auth.AzureAuthConfig{
 			Cloud: "AZUREPUBLICCLOUD",
 		},
 		CloudConfigType: cloudConfigTypeFile,